Added support for loading uncompressed animations and playing them back
This commit is contained in:
parent
2376a36f0b
commit
ef523c2339
|
@ -188,7 +188,9 @@ HEADERS += \
|
||||||
Resource/Script/CLink.h \
|
Resource/Script/CLink.h \
|
||||||
Resource/CSkeleton.h \
|
Resource/CSkeleton.h \
|
||||||
Resource/Factory/CSkeletonLoader.h \
|
Resource/Factory/CSkeletonLoader.h \
|
||||||
Scene/CCharacterNode.h
|
Scene/CCharacterNode.h \
|
||||||
|
Resource/CAnimation.h \
|
||||||
|
Resource/Factory/CAnimationLoader.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
@ -269,4 +271,6 @@ SOURCES += \
|
||||||
ScriptExtra/CSplinePathExtra.cpp \
|
ScriptExtra/CSplinePathExtra.cpp \
|
||||||
Resource/CSkeleton.cpp \
|
Resource/CSkeleton.cpp \
|
||||||
Resource/Factory/CSkeletonLoader.cpp \
|
Resource/Factory/CSkeletonLoader.cpp \
|
||||||
Scene/CCharacterNode.cpp
|
Scene/CCharacterNode.cpp \
|
||||||
|
Resource/CAnimation.cpp \
|
||||||
|
Resource/Factory/CAnimationLoader.cpp
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define CANIMSET_H
|
#define CANIMSET_H
|
||||||
|
|
||||||
#include "TResPtr.h"
|
#include "TResPtr.h"
|
||||||
|
#include "CAnimation.h"
|
||||||
#include "CResource.h"
|
#include "CResource.h"
|
||||||
#include "CSkeleton.h"
|
#include "CSkeleton.h"
|
||||||
#include "Core/Resource/Model/CModel.h"
|
#include "Core/Resource/Model/CModel.h"
|
||||||
|
@ -26,6 +27,13 @@ class CAnimSet : public CResource
|
||||||
};
|
};
|
||||||
std::vector<SNode> mNodes;
|
std::vector<SNode> mNodes;
|
||||||
|
|
||||||
|
struct SAnimation
|
||||||
|
{
|
||||||
|
TString Name;
|
||||||
|
TResPtr<CAnimation> pAnim;
|
||||||
|
};
|
||||||
|
std::vector<SAnimation> mAnims;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAnimSet() : CResource() {}
|
CAnimSet() : CResource() {}
|
||||||
|
|
||||||
|
@ -33,6 +41,10 @@ public:
|
||||||
TString NodeName(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].Name; }
|
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; }
|
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; }
|
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
|
#endif // CCHARACTERSET_H
|
||||||
|
|
|
@ -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) );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 "CResCache.h"
|
||||||
#include "Core/Resource/Factory/CAreaLoader.h"
|
#include "Core/Resource/Factory/CAreaLoader.h"
|
||||||
|
#include "Core/Resource/Factory/CAnimationLoader.h"
|
||||||
#include "Core/Resource/Factory/CAnimSetLoader.h"
|
#include "Core/Resource/Factory/CAnimSetLoader.h"
|
||||||
#include "Core/Resource/Factory/CCollisionLoader.h"
|
#include "Core/Resource/Factory/CCollisionLoader.h"
|
||||||
#include "Core/Resource/Factory/CFontLoader.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 == "DCLN") pRes = CCollisionLoader::LoadDCLN(Mem);
|
||||||
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(Mem);
|
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(Mem);
|
||||||
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(Mem);
|
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(Mem);
|
||||||
|
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(Mem);
|
||||||
else SupportedFormat = false;
|
else SupportedFormat = false;
|
||||||
|
|
||||||
// Log errors
|
// Log errors
|
||||||
|
@ -206,6 +208,7 @@ CResource* CResCache::GetResource(const TString& rkResPath)
|
||||||
else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(File);
|
else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(File);
|
||||||
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(File);
|
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(File);
|
||||||
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(File);
|
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(File);
|
||||||
|
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(File);
|
||||||
else SupportedFormat = false;
|
else SupportedFormat = false;
|
||||||
|
|
||||||
if (!pRes) pRes = new CResource(); // Default for unsupported formats
|
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::CSkeleton()
|
CSkeleton::CSkeleton()
|
||||||
: mpRootBone(nullptr)
|
: mpRootBone(nullptr)
|
||||||
|
@ -31,6 +68,11 @@ CBone* CSkeleton::BoneByID(u32 BoneID) const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSkeleton::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot)
|
||||||
|
{
|
||||||
|
mpRootBone->UpdateTransform(pAnim, Time, AnchorRoot);
|
||||||
|
}
|
||||||
|
|
||||||
void CSkeleton::Draw(FRenderOptions /*Options*/)
|
void CSkeleton::Draw(FRenderOptions /*Options*/)
|
||||||
{
|
{
|
||||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||||
|
@ -39,8 +81,8 @@ void CSkeleton::Draw(FRenderOptions /*Options*/)
|
||||||
|
|
||||||
// Draw bone
|
// Draw bone
|
||||||
CTransform4f Transform;
|
CTransform4f Transform;
|
||||||
Transform.Scale(0.01f);
|
Transform.Scale(0.025f);
|
||||||
Transform.Translate(pBone->Position());
|
Transform.Translate(pBone->AbsolutePosition());
|
||||||
CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f();
|
CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f();
|
||||||
CGraphics::UpdateMVPBlock();
|
CGraphics::UpdateMVPBlock();
|
||||||
CDrawUtil::DrawSphere(CColor::skWhite);
|
CDrawUtil::DrawSphere(CColor::skWhite);
|
||||||
|
@ -50,6 +92,6 @@ void CSkeleton::Draw(FRenderOptions /*Options*/)
|
||||||
CGraphics::UpdateMVPBlock();
|
CGraphics::UpdateMVPBlock();
|
||||||
|
|
||||||
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
|
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
|
#ifndef CSKELETON_H
|
||||||
#define CSKELETON_H
|
#define CSKELETON_H
|
||||||
|
|
||||||
|
#include "CAnimation.h"
|
||||||
#include "CResource.h"
|
#include "CResource.h"
|
||||||
#include "Core/Render/FRenderOptions.h"
|
#include "Core/Render/FRenderOptions.h"
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
#include <Common/types.h>
|
#include <Common/types.h>
|
||||||
|
#include <Math/CTransform4f.h>
|
||||||
#include <Math/CVector3f.h>
|
#include <Math/CVector3f.h>
|
||||||
|
|
||||||
class CSkeleton;
|
class CSkeleton;
|
||||||
|
@ -20,14 +22,24 @@ class CBone
|
||||||
CVector3f mPosition;
|
CVector3f mPosition;
|
||||||
TString mName;
|
TString mName;
|
||||||
|
|
||||||
|
CTransform4f mAnimTransform;
|
||||||
|
mutable bool mAbsPosDirty;
|
||||||
|
mutable CVector3f mAbsolutePosition;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CBone(CSkeleton *pSkel);
|
CBone(CSkeleton *pSkel);
|
||||||
|
void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot);
|
||||||
|
bool IsRoot() const;
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
inline u32 ID() const { return mID; }
|
inline u32 ID() const { return mID; }
|
||||||
inline CVector3f Position() const { return mPosition; }
|
inline CVector3f Position() const { return mPosition; }
|
||||||
|
inline CBone* Parent() const { return mpParent; }
|
||||||
inline u32 NumChildren() const { return mChildren.size(); }
|
inline u32 NumChildren() const { return mChildren.size(); }
|
||||||
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
|
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
|
||||||
|
inline const CTransform4f& AnimTransform() const { return mAnimTransform; }
|
||||||
|
|
||||||
|
CVector3f AbsolutePosition() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CSkeleton : public CResource
|
class CSkeleton : public CResource
|
||||||
|
@ -41,7 +53,7 @@ class CSkeleton : public CResource
|
||||||
public:
|
public:
|
||||||
CSkeleton();
|
CSkeleton();
|
||||||
~CSkeleton();
|
~CSkeleton();
|
||||||
|
void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot);
|
||||||
CBone* BoneByID(u32 BoneID) const;
|
CBone* BoneByID(u32 BoneID) const;
|
||||||
void Draw(FRenderOptions Options);
|
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.
|
// 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
|
// 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();
|
u32 AnimCount = rANCS.ReadLong();
|
||||||
|
bool ReadAnimNames = Loader.pSet->mAnims.empty();
|
||||||
|
if (ReadAnimNames) Loader.pSet->mAnims.resize(AnimCount);
|
||||||
|
|
||||||
for (u32 iAnim = 0; iAnim < AnimCount; iAnim++)
|
for (u32 iAnim = 0; iAnim < AnimCount; iAnim++)
|
||||||
{
|
{
|
||||||
rANCS.Seek(0x4, SEEK_CUR);
|
rANCS.Seek(0x4, SEEK_CUR);
|
||||||
if (Loader.mVersion == ePrime) rANCS.Seek(0x1, 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
|
// PAS Database
|
||||||
|
@ -165,7 +169,6 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS)
|
||||||
u32 UnknownCount2 = rANCS.ReadLong();
|
u32 UnknownCount2 = rANCS.ReadLong();
|
||||||
rANCS.Seek(UnknownCount2 * 0x1C, SEEK_CUR);
|
rANCS.Seek(UnknownCount2 * 0x1C, SEEK_CUR);
|
||||||
}
|
}
|
||||||
// Lots of work for data I'm not even using x.x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Loader.pSet;
|
return Loader.pSet;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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>
|
#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 ************
|
// ************ STATIC ************
|
||||||
CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
|
CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
|
||||||
{
|
{
|
||||||
|
CSkeletonLoader Loader;
|
||||||
CSkeleton *pSkel = new CSkeleton();
|
CSkeleton *pSkel = new CSkeleton();
|
||||||
|
|
||||||
u32 NumBones = rCINF.ReadLong();
|
u32 NumBones = rCINF.ReadLong();
|
||||||
|
@ -71,6 +77,8 @@ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader.SetLocalBoneCoords(pSkel->mpRootBone);
|
||||||
|
|
||||||
// Skip bone ID array
|
// Skip bone ID array
|
||||||
u32 NumBoneIDs = rCINF.ReadLong();
|
u32 NumBoneIDs = rCINF.ReadLong();
|
||||||
rCINF.Seek(NumBoneIDs * 4, SEEK_CUR);
|
rCINF.Seek(NumBoneIDs * 4, SEEK_CUR);
|
||||||
|
|
|
@ -10,7 +10,8 @@ class CSkeletonLoader
|
||||||
TResPtr<CSkeleton> mpSkeleton;
|
TResPtr<CSkeleton> mpSkeleton;
|
||||||
EGame mVersion;
|
EGame mVersion;
|
||||||
|
|
||||||
CSkeletonLoader();
|
CSkeletonLoader() {}
|
||||||
|
void SetLocalBoneCoords(CBone *pBone);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static CSkeleton* LoadCINF(IInputStream& rCINF);
|
static CSkeleton* LoadCINF(IInputStream& rCINF);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "CCharacterNode.h"
|
#include "CCharacterNode.h"
|
||||||
#include <Core/Render/CRenderer.h>
|
#include "Core/Render/CRenderer.h"
|
||||||
|
#include <Common/CTimer.h>
|
||||||
|
|
||||||
CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/)
|
CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/)
|
||||||
: CSceneNode(pScene, NodeID, pParent)
|
: CSceneNode(pScene, NodeID, pParent)
|
||||||
|
@ -34,6 +35,8 @@ void CCharacterNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& /*rkVi
|
||||||
void CCharacterNode::Draw(FRenderOptions Options, int /*ComponentIndex*/, const SViewInfo& /*rkViewInfo*/)
|
void CCharacterNode::Draw(FRenderOptions Options, int /*ComponentIndex*/, const SViewInfo& /*rkViewInfo*/)
|
||||||
{
|
{
|
||||||
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
||||||
|
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
|
||||||
|
pSkel->UpdateTransform(pAnim, (float) CTimer::GlobalTime(), false);
|
||||||
pSkel->Draw(Options);
|
pSkel->Draw(Options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,3 +65,8 @@ void CCharacterNode::SetActiveCharSet(u32 CharIndex)
|
||||||
MarkTransformChanged();
|
MarkTransformChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCharacterNode::SetActiveAnim(u32 AnimIndex)
|
||||||
|
{
|
||||||
|
mActiveAnim = AnimIndex;
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ class CCharacterNode : public CSceneNode
|
||||||
{
|
{
|
||||||
TResPtr<CAnimSet> mpCharacter;
|
TResPtr<CAnimSet> mpCharacter;
|
||||||
u32 mActiveCharSet;
|
u32 mActiveCharSet;
|
||||||
|
u32 mActiveAnim;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar = 0, CSceneNode *pParent = 0);
|
explicit CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar = 0, CSceneNode *pParent = 0);
|
||||||
|
@ -19,9 +20,11 @@ public:
|
||||||
virtual SRayIntersection RayNodeIntersectTest(const CRay& rkRay, u32 AssetID, const SViewInfo& rkViewInfo);
|
virtual SRayIntersection RayNodeIntersectTest(const CRay& rkRay, u32 AssetID, const SViewInfo& rkViewInfo);
|
||||||
inline CAnimSet* Character() const { return mpCharacter; }
|
inline CAnimSet* Character() const { return mpCharacter; }
|
||||||
inline u32 ActiveCharSet() const { return mActiveCharSet; }
|
inline u32 ActiveCharSet() const { return mActiveCharSet; }
|
||||||
|
inline u32 ActiveAnim() const { return mActiveAnim; }
|
||||||
|
|
||||||
void SetCharacter(CAnimSet *pChar);
|
void SetCharacter(CAnimSet *pChar);
|
||||||
void SetActiveCharSet(u32 CharIndex);
|
void SetActiveCharSet(u32 CharIndex);
|
||||||
|
void SetActiveAnim(u32 AnimIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CCHARACTERNODE_H
|
#endif // CCHARACTERNODE_H
|
||||||
|
|
|
@ -19,15 +19,21 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
|
||||||
rCamera.SetMoveSpeed(0.5f);
|
rCamera.SetMoveSpeed(0.5f);
|
||||||
|
|
||||||
// Init UI
|
// Init UI
|
||||||
|
ui->ToolBar->addSeparator();
|
||||||
|
|
||||||
mpCharComboBox = new QComboBox(this);
|
mpCharComboBox = new QComboBox(this);
|
||||||
mpCharComboBox->setMinimumWidth(175);
|
mpCharComboBox->setMinimumWidth(175);
|
||||||
ui->ToolBar->addSeparator();
|
|
||||||
ui->ToolBar->addWidget(mpCharComboBox);
|
ui->ToolBar->addWidget(mpCharComboBox);
|
||||||
|
|
||||||
|
mpAnimComboBox = new QComboBox(this);
|
||||||
|
mpAnimComboBox->setMinimumWidth(175);
|
||||||
|
ui->ToolBar->addWidget(mpAnimComboBox);
|
||||||
|
|
||||||
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
|
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
|
||||||
mRefreshTimer.start(0);
|
mRefreshTimer.start(0);
|
||||||
|
|
||||||
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
|
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
|
||||||
|
connect(mpAnimComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveAnimation(int)));
|
||||||
connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open()));
|
connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +64,16 @@ void CCharacterEditor::Open()
|
||||||
|
|
||||||
SetActiveCharacterIndex(0);
|
SetActiveCharacterIndex(0);
|
||||||
mpCharComboBox->blockSignals(false);
|
mpCharComboBox->blockSignals(false);
|
||||||
|
|
||||||
|
// Set up anim combo box
|
||||||
|
mpAnimComboBox->blockSignals(true);
|
||||||
|
mpAnimComboBox->clear();
|
||||||
|
|
||||||
|
for (u32 iAnim = 0; iAnim < pSet->NumAnims(); iAnim++)
|
||||||
|
mpAnimComboBox->addItem( TO_QSTRING(pSet->AnimName(iAnim)) );
|
||||||
|
|
||||||
|
SetActiveAnimation(0);
|
||||||
|
mpAnimComboBox->blockSignals(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
gResCache.Clean();
|
gResCache.Clean();
|
||||||
|
@ -73,3 +89,8 @@ void CCharacterEditor::SetActiveCharacterIndex(int CharIndex)
|
||||||
{
|
{
|
||||||
mpCharNode->SetActiveCharSet((u32) CharIndex);
|
mpCharNode->SetActiveCharSet((u32) CharIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::SetActiveAnimation(int AnimIndex)
|
||||||
|
{
|
||||||
|
mpCharNode->SetActiveAnim((u32) AnimIndex);
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ class CCharacterEditor : public QMainWindow
|
||||||
CCharacterNode *mpCharNode;
|
CCharacterNode *mpCharNode;
|
||||||
|
|
||||||
QComboBox *mpCharComboBox;
|
QComboBox *mpCharComboBox;
|
||||||
|
QComboBox *mpAnimComboBox;
|
||||||
QTimer mRefreshTimer;
|
QTimer mRefreshTimer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -32,6 +33,7 @@ public slots:
|
||||||
void Open();
|
void Open();
|
||||||
void RefreshViewport();
|
void RefreshViewport();
|
||||||
void SetActiveCharacterIndex(int CharIndex);
|
void SetActiveCharacterIndex(int CharIndex);
|
||||||
|
void SetActiveAnimation(int AnimIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CCHARACTEREDITORWINDOW_H
|
#endif // CCHARACTEREDITORWINDOW_H
|
||||||
|
|
|
@ -41,7 +41,8 @@ HEADERS += \
|
||||||
FileIO.h \
|
FileIO.h \
|
||||||
IOUtil.h \
|
IOUtil.h \
|
||||||
IInputStream.h \
|
IInputStream.h \
|
||||||
IOutputStream.h
|
IOutputStream.h \
|
||||||
|
CBitStreamInWrapper.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
@ -54,4 +55,5 @@ SOURCES += \
|
||||||
CVectorOutStream.cpp \
|
CVectorOutStream.cpp \
|
||||||
IOUtil.cpp \
|
IOUtil.cpp \
|
||||||
IInputStream.cpp \
|
IInputStream.cpp \
|
||||||
IOutputStream.cpp
|
IOutputStream.cpp \
|
||||||
|
CBitStreamInWrapper.cpp
|
||||||
|
|
|
@ -20,6 +20,14 @@ CQuaternion::CQuaternion(float _W, float _X, float _Y, float _Z)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CQuaternion::CQuaternion(IInputStream& rInput)
|
||||||
|
: W(rInput.ReadFloat())
|
||||||
|
, X(rInput.ReadFloat())
|
||||||
|
, Y(rInput.ReadFloat())
|
||||||
|
, Z(rInput.ReadFloat())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CVector3f CQuaternion::XAxis() const
|
CVector3f CQuaternion::XAxis() const
|
||||||
{
|
{
|
||||||
return (*this * CVector3f::skUnitX);
|
return (*this * CVector3f::skUnitX);
|
||||||
|
@ -48,6 +56,36 @@ CQuaternion CQuaternion::Inverse() const
|
||||||
return CQuaternion::skZero;
|
return CQuaternion::skZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CQuaternion CQuaternion::Slerp(const CQuaternion& rkRight, float t) const
|
||||||
|
{
|
||||||
|
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
|
||||||
|
float CosHalfTheta = (W * rkRight.W) + (X * rkRight.X) + (Y * rkRight.Y) + (Z * rkRight.Z);
|
||||||
|
|
||||||
|
if (Math::Abs(CosHalfTheta) >= 1.f)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
float ScalarA, ScalarB;
|
||||||
|
float SinHalfTheta = Math::Sqrt(1.f - (CosHalfTheta * CosHalfTheta));
|
||||||
|
|
||||||
|
if (Math::Abs(SinHalfTheta) < 0.001f)
|
||||||
|
{
|
||||||
|
ScalarA = 0.5f;
|
||||||
|
ScalarB = 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float HalfTheta = acosf(CosHalfTheta);
|
||||||
|
ScalarA = sinf((1.f - t) * HalfTheta) / SinHalfTheta;
|
||||||
|
ScalarB = sinf(t * HalfTheta) / SinHalfTheta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CQuaternion( (W * ScalarA) + (rkRight.W * ScalarB),
|
||||||
|
(X * ScalarA) + (rkRight.X * ScalarB),
|
||||||
|
(Y * ScalarA) + (rkRight.Y * ScalarB),
|
||||||
|
(Z * ScalarA) + (rkRight.Z * ScalarB) );
|
||||||
|
}
|
||||||
|
|
||||||
CVector3f CQuaternion::ToEuler() const
|
CVector3f CQuaternion::ToEuler() const
|
||||||
{
|
{
|
||||||
// There is more than one way to do this conversion, based on rotation order.
|
// There is more than one way to do this conversion, based on rotation order.
|
||||||
|
|
|
@ -10,11 +10,13 @@ public:
|
||||||
|
|
||||||
CQuaternion();
|
CQuaternion();
|
||||||
CQuaternion(float _W, float _X, float _Y, float _Z);
|
CQuaternion(float _W, float _X, float _Y, float _Z);
|
||||||
|
CQuaternion(IInputStream& rInput);
|
||||||
|
|
||||||
CVector3f XAxis() const;
|
CVector3f XAxis() const;
|
||||||
CVector3f YAxis() const;
|
CVector3f YAxis() const;
|
||||||
CVector3f ZAxis() const;
|
CVector3f ZAxis() const;
|
||||||
CQuaternion Inverse() const;
|
CQuaternion Inverse() const;
|
||||||
|
CQuaternion Slerp(const CQuaternion& rkRight, float t) const;
|
||||||
CVector3f ToEuler() const;
|
CVector3f ToEuler() const;
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
|
|
@ -100,6 +100,13 @@ void CTransform4f::Scale(float XScale, float YScale, float ZScale)
|
||||||
Scale(CVector3f(XScale, YScale, ZScale));
|
Scale(CVector3f(XScale, YScale, ZScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CTransform4f::ZeroTranslation()
|
||||||
|
{
|
||||||
|
m[0][3] = 0.f;
|
||||||
|
m[1][3] = 0.f;
|
||||||
|
m[2][3] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
CTransform4f CTransform4f::MultiplyIgnoreTranslation(const CTransform4f& rkMtx) const
|
CTransform4f CTransform4f::MultiplyIgnoreTranslation(const CTransform4f& rkMtx) const
|
||||||
{
|
{
|
||||||
CTransform4f Out;
|
CTransform4f Out;
|
||||||
|
@ -153,6 +160,13 @@ CTransform4f CTransform4f::NoTranslation() const
|
||||||
m[2][0], m[2][1], m[2][2], 0.f);
|
m[2][0], m[2][1], m[2][2], 0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CTransform4f CTransform4f::TranslationOnly() const
|
||||||
|
{
|
||||||
|
return CTransform4f(1.f, 0.f, 0.f, m[0][3],
|
||||||
|
0.f, 1.f, 0.f, m[1][3],
|
||||||
|
0.f, 0.f, 1.f, m[2][3]);
|
||||||
|
}
|
||||||
|
|
||||||
CTransform4f CTransform4f::RotationOnly() const
|
CTransform4f CTransform4f::RotationOnly() const
|
||||||
{
|
{
|
||||||
return CTransform4f::FromMatrix4f(Inverse().ToMatrix4f().Transpose());
|
return CTransform4f::FromMatrix4f(Inverse().ToMatrix4f().Transpose());
|
||||||
|
|
|
@ -37,10 +37,12 @@ public:
|
||||||
void Rotate(float XRot, float YRot, float ZRot);
|
void Rotate(float XRot, float YRot, float ZRot);
|
||||||
void Scale(CVector3f Scale);
|
void Scale(CVector3f Scale);
|
||||||
void Scale(float XScale, float YScale, float ZScale);
|
void Scale(float XScale, float YScale, float ZScale);
|
||||||
|
void ZeroTranslation();
|
||||||
CTransform4f MultiplyIgnoreTranslation(const CTransform4f& rkMtx) const;
|
CTransform4f MultiplyIgnoreTranslation(const CTransform4f& rkMtx) const;
|
||||||
CTransform4f Inverse() const;
|
CTransform4f Inverse() const;
|
||||||
CTransform4f QuickInverse() const;
|
CTransform4f QuickInverse() const;
|
||||||
CTransform4f NoTranslation() const;
|
CTransform4f NoTranslation() const;
|
||||||
|
CTransform4f TranslationOnly() const;
|
||||||
CTransform4f RotationOnly() const;
|
CTransform4f RotationOnly() const;
|
||||||
|
|
||||||
// Conversion
|
// Conversion
|
||||||
|
|
|
@ -22,6 +22,13 @@ float DegreesToRadians(float Deg);
|
||||||
|
|
||||||
float RadiansToDegrees(float Rad);
|
float RadiansToDegrees(float Rad);
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
Type Lerp(const Type& rkA, const Type& rkB, float t)
|
||||||
|
{
|
||||||
|
Type Diff = rkB - rkA;
|
||||||
|
return rkA + (Diff * t);
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<bool,float> RayPlaneIntersecton(const CRay& rkRay, const CPlane& rkPlane);
|
std::pair<bool,float> RayPlaneIntersecton(const CRay& rkRay, const CPlane& rkPlane);
|
||||||
|
|
||||||
std::pair<bool,float> RayBoxIntersection(const CRay& rkRay, const CAABox& rkBox);
|
std::pair<bool,float> RayBoxIntersection(const CRay& rkRay, const CAABox& rkBox);
|
||||||
|
|
Loading…
Reference in New Issue