Added support for loading compressed animations
This commit is contained in:
parent
ef523c2339
commit
a931e2aec6
|
@ -26,7 +26,7 @@ class CAnimation : public CResource
|
||||||
u8 RotationChannelIdx;
|
u8 RotationChannelIdx;
|
||||||
u8 TranslationChannelIdx;
|
u8 TranslationChannelIdx;
|
||||||
};
|
};
|
||||||
std::vector<SBoneChannelInfo> mBoneInfo;
|
SBoneChannelInfo mBoneInfo[100];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAnimation();
|
CAnimation();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "CAnimationLoader.h"
|
#include "CAnimationLoader.h"
|
||||||
#include <Common/Log.h>
|
#include <Common/Log.h>
|
||||||
|
#include <Math/MathUtil.h>
|
||||||
|
|
||||||
void CAnimationLoader::ReadUncompressedANIM()
|
void CAnimationLoader::ReadUncompressedANIM()
|
||||||
{
|
{
|
||||||
|
@ -38,8 +39,6 @@ void CAnimationLoader::ReadUncompressedANIM()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up bone channel info
|
// Set up bone channel info
|
||||||
mpAnim->mBoneInfo.resize(NumRotIndices);
|
|
||||||
|
|
||||||
for (u32 iRot = 0, iTrans = 0; iRot < NumRotIndices; iRot++)
|
for (u32 iRot = 0, iTrans = 0; iRot < NumRotIndices; iRot++)
|
||||||
{
|
{
|
||||||
u8 RotIdx = RotIndices[iRot];
|
u8 RotIdx = RotIndices[iRot];
|
||||||
|
@ -80,6 +79,203 @@ void CAnimationLoader::ReadUncompressedANIM()
|
||||||
// Skip EVNT file
|
// Skip EVNT file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CAnimationLoader::ReadCompressedANIM()
|
||||||
|
{
|
||||||
|
// Header
|
||||||
|
mpInput->Seek(0xC, SEEK_CUR); // Skip alloc size, EVNT ID, unknown value
|
||||||
|
mpAnim->mDuration = mpInput->ReadFloat();
|
||||||
|
mpAnim->mTickInterval = mpInput->ReadFloat();
|
||||||
|
mpInput->Seek(0x8, SEEK_CUR); // Skip two unknown values
|
||||||
|
|
||||||
|
mRotationDivisor = mpInput->ReadLong();
|
||||||
|
mTranslationMultiplier = mpInput->ReadFloat();
|
||||||
|
u32 NumBoneChannels = mpInput->ReadLong();
|
||||||
|
mpInput->Seek(0x4, SEEK_CUR); // Skip unknown value
|
||||||
|
|
||||||
|
// Read key flags
|
||||||
|
u32 NumKeys = mpInput->ReadLong();
|
||||||
|
mpAnim->mNumKeys = NumKeys - 1;
|
||||||
|
mKeyFlags.resize(NumKeys);
|
||||||
|
{
|
||||||
|
CBitStreamInWrapper BitStream(mpInput);
|
||||||
|
|
||||||
|
for (u32 iBit = 0; iBit < NumKeys; iBit++)
|
||||||
|
mKeyFlags[iBit] = BitStream.ReadBit();
|
||||||
|
}
|
||||||
|
mpInput->Seek(0x8, SEEK_CUR);
|
||||||
|
|
||||||
|
// Read bone channel descriptors
|
||||||
|
mCompressedChannels.resize(NumBoneChannels);
|
||||||
|
mpAnim->mRotationChannels.resize(NumBoneChannels);
|
||||||
|
mpAnim->mTranslationChannels.resize(NumBoneChannels);
|
||||||
|
|
||||||
|
for (u32 iChan = 0; iChan < NumBoneChannels; iChan++)
|
||||||
|
{
|
||||||
|
SCompressedChannel& rChan = mCompressedChannels[iChan];
|
||||||
|
rChan.BoneID = mpInput->ReadLong();
|
||||||
|
|
||||||
|
// Read rotation parameters
|
||||||
|
rChan.NumRotationKeys = mpInput->ReadShort();
|
||||||
|
|
||||||
|
if (rChan.NumRotationKeys > 0)
|
||||||
|
{
|
||||||
|
for (u32 iComp = 0; iComp < 3; iComp++)
|
||||||
|
{
|
||||||
|
rChan.Rotation[iComp] = mpInput->ReadShort();
|
||||||
|
rChan.RotationBits[iComp] = mpInput->ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
mpAnim->mBoneInfo[rChan.BoneID].RotationChannelIdx = (u8) iChan;
|
||||||
|
}
|
||||||
|
else mpAnim->mBoneInfo[rChan.BoneID].RotationChannelIdx = 0xFF;
|
||||||
|
|
||||||
|
// Read translation parameters
|
||||||
|
rChan.NumTranslationKeys = mpInput->ReadShort();
|
||||||
|
|
||||||
|
if (rChan.NumTranslationKeys > 0)
|
||||||
|
{
|
||||||
|
for (u32 iComp = 0; iComp < 3; iComp++)
|
||||||
|
{
|
||||||
|
rChan.Translation[iComp] = mpInput->ReadShort();
|
||||||
|
rChan.TranslationBits[iComp] = mpInput->ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
mpAnim->mBoneInfo[rChan.BoneID].TranslationChannelIdx = (u8) iChan;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mpAnim->mBoneInfo[rChan.BoneID].TranslationChannelIdx = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read animation data
|
||||||
|
ReadCompressedAnimationData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAnimationLoader::ReadCompressedAnimationData()
|
||||||
|
{
|
||||||
|
CBitStreamInWrapper BitStream(mpInput);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
|
||||||
|
{
|
||||||
|
SCompressedChannel& rChan = mCompressedChannels[iChan];
|
||||||
|
|
||||||
|
// Reserve memory for all keys
|
||||||
|
|
||||||
|
// Set initial rotation/translation
|
||||||
|
if (rChan.NumRotationKeys > 0)
|
||||||
|
{
|
||||||
|
mpAnim->mRotationChannels[iChan].reserve(rChan.NumRotationKeys + 1);
|
||||||
|
CQuaternion Rotation = DequantizeRotation(false, rChan.Rotation[0], rChan.Rotation[1], rChan.Rotation[2]);
|
||||||
|
mpAnim->mRotationChannels[iChan].push_back(Rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rChan.NumTranslationKeys > 0)
|
||||||
|
{
|
||||||
|
mpAnim->mTranslationChannels[iChan].reserve(rChan.NumTranslationKeys + 1);
|
||||||
|
CVector3f Translate = CVector3f(rChan.Translation[0], rChan.Translation[1], rChan.Translation[2]) * mTranslationMultiplier;
|
||||||
|
mpAnim->mTranslationChannels[iChan].push_back(Translate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read keys
|
||||||
|
for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++)
|
||||||
|
{
|
||||||
|
bool KeyPresent = mKeyFlags[iKey];
|
||||||
|
|
||||||
|
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
|
||||||
|
{
|
||||||
|
SCompressedChannel& rChan = mCompressedChannels[iChan];
|
||||||
|
|
||||||
|
// Read rotation
|
||||||
|
if (rChan.NumRotationKeys > 0)
|
||||||
|
{
|
||||||
|
bool WSign = (KeyPresent ? BitStream.ReadBit() : false);
|
||||||
|
|
||||||
|
if (KeyPresent)
|
||||||
|
{
|
||||||
|
rChan.Rotation[0] += (s16) BitStream.ReadBits(rChan.RotationBits[0]);
|
||||||
|
rChan.Rotation[1] += (s16) BitStream.ReadBits(rChan.RotationBits[1]);
|
||||||
|
rChan.Rotation[2] += (s16) BitStream.ReadBits(rChan.RotationBits[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
CQuaternion Rotation = DequantizeRotation(WSign, rChan.Rotation[0], rChan.Rotation[1], rChan.Rotation[2]);
|
||||||
|
mpAnim->mRotationChannels[iChan].push_back(Rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read translation
|
||||||
|
if (rChan.NumTranslationKeys > 0)
|
||||||
|
{
|
||||||
|
if (KeyPresent)
|
||||||
|
{
|
||||||
|
rChan.Translation[0] += (s16) BitStream.ReadBits(rChan.TranslationBits[0]);
|
||||||
|
rChan.Translation[1] += (s16) BitStream.ReadBits(rChan.TranslationBits[1]);
|
||||||
|
rChan.Translation[2] += (s16) BitStream.ReadBits(rChan.TranslationBits[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector3f Translate = CVector3f(rChan.Translation[0], rChan.Translation[1], rChan.Translation[2]) * mTranslationMultiplier;
|
||||||
|
mpAnim->mTranslationChannels[iChan].push_back(Translate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in missing keys
|
||||||
|
u32 NumMissedKeys = 0;
|
||||||
|
|
||||||
|
for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++)
|
||||||
|
{
|
||||||
|
if (!mKeyFlags[iKey])
|
||||||
|
NumMissedKeys++;
|
||||||
|
|
||||||
|
else if (NumMissedKeys > 0)
|
||||||
|
{
|
||||||
|
u32 FirstIndex = iKey - NumMissedKeys - 1;
|
||||||
|
u32 LastIndex = iKey;
|
||||||
|
u32 RelLastIndex = LastIndex - FirstIndex;
|
||||||
|
|
||||||
|
for (u32 iMissed = 0; iMissed < NumMissedKeys; iMissed++)
|
||||||
|
{
|
||||||
|
u32 KeyIndex = FirstIndex + iMissed + 1;
|
||||||
|
u32 RelKeyIndex = (KeyIndex - FirstIndex);
|
||||||
|
|
||||||
|
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
|
||||||
|
{
|
||||||
|
bool HasTranslationKeys = mCompressedChannels[iChan].NumTranslationKeys > 0;
|
||||||
|
bool HasRotationKeys = mCompressedChannels[iChan].NumRotationKeys > 0;
|
||||||
|
float Interp = (float) RelKeyIndex / (float) RelLastIndex;
|
||||||
|
|
||||||
|
if (HasRotationKeys)
|
||||||
|
{
|
||||||
|
CQuaternion Left = mpAnim->mRotationChannels[iChan][FirstIndex];
|
||||||
|
CQuaternion Right = mpAnim->mRotationChannels[iChan][LastIndex];
|
||||||
|
mpAnim->mRotationChannels[iChan][KeyIndex] = Left.Slerp(Right, Interp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasTranslationKeys)
|
||||||
|
{
|
||||||
|
CVector3f Left = mpAnim->mTranslationChannels[iChan][FirstIndex];
|
||||||
|
CVector3f Right = mpAnim->mTranslationChannels[iChan][LastIndex];
|
||||||
|
mpAnim->mTranslationChannels[iChan][KeyIndex] = Math::Lerp<CVector3f>(Left, Right, Interp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumMissedKeys = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CQuaternion CAnimationLoader::DequantizeRotation(bool Sign, s16 X, s16 Y, s16 Z)
|
||||||
|
{
|
||||||
|
CQuaternion Out;
|
||||||
|
float Multiplier = Math::skHalfPi / (float) mRotationDivisor;
|
||||||
|
Out.X = sinf(X * Multiplier);
|
||||||
|
Out.Y = sinf(Y * Multiplier);
|
||||||
|
Out.Z = sinf(Z * Multiplier);
|
||||||
|
Out.W = Math::Sqrt( fmax(1.f - ((Out.X * Out.X) + (Out.Y * Out.Y) + (Out.Z * Out.Z)), 0.f) );
|
||||||
|
if (Sign) Out.W = -Out.W;
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
// ************ STATIC ************
|
// ************ STATIC ************
|
||||||
CAnimation* CAnimationLoader::LoadANIM(IInputStream& rANIM)
|
CAnimation* CAnimationLoader::LoadANIM(IInputStream& rANIM)
|
||||||
{
|
{
|
||||||
|
@ -91,15 +287,14 @@ CAnimation* CAnimationLoader::LoadANIM(IInputStream& rANIM)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CompressionType == 2)
|
|
||||||
{
|
|
||||||
Log::FileError(rANIM.GetSourceString(), "Compressed ANIMs not supported yet");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
CAnimationLoader Loader;
|
CAnimationLoader Loader;
|
||||||
Loader.mpAnim = new CAnimation();
|
Loader.mpAnim = new CAnimation();
|
||||||
Loader.mpInput = &rANIM;
|
Loader.mpInput = &rANIM;
|
||||||
Loader.ReadUncompressedANIM();
|
|
||||||
|
if (CompressionType == 0)
|
||||||
|
Loader.ReadUncompressedANIM();
|
||||||
|
else
|
||||||
|
Loader.ReadCompressedANIM();
|
||||||
|
|
||||||
return Loader.mpAnim;
|
return Loader.mpAnim;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,28 @@ class CAnimationLoader
|
||||||
TResPtr<CAnimation> mpAnim;
|
TResPtr<CAnimation> mpAnim;
|
||||||
IInputStream *mpInput;
|
IInputStream *mpInput;
|
||||||
|
|
||||||
|
// Compression data
|
||||||
|
std::vector<bool> mKeyFlags;
|
||||||
|
float mTranslationMultiplier;
|
||||||
|
u32 mRotationDivisor;
|
||||||
|
|
||||||
|
struct SCompressedChannel
|
||||||
|
{
|
||||||
|
u32 BoneID;
|
||||||
|
u16 NumRotationKeys;
|
||||||
|
s16 Rotation[3];
|
||||||
|
u8 RotationBits[3];
|
||||||
|
u16 NumTranslationKeys;
|
||||||
|
s16 Translation[3];
|
||||||
|
u8 TranslationBits[3];
|
||||||
|
};
|
||||||
|
std::vector<SCompressedChannel> mCompressedChannels;
|
||||||
|
|
||||||
CAnimationLoader() {}
|
CAnimationLoader() {}
|
||||||
void ReadUncompressedANIM();
|
void ReadUncompressedANIM();
|
||||||
|
void ReadCompressedANIM();
|
||||||
|
void ReadCompressedAnimationData();
|
||||||
|
CQuaternion DequantizeRotation(bool Sign, s16 X, s16 Y, s16 Z);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static CAnimation* LoadANIM(IInputStream& rANIM);
|
static CAnimation* LoadANIM(IInputStream& rANIM);
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "CBitStreamInWrapper.h"
|
||||||
|
|
||||||
|
CBitStreamInWrapper::CBitStreamInWrapper(IInputStream *pStream, EChunkSize ChunkSize /*= e32Bit*/)
|
||||||
|
: mpSourceStream(pStream)
|
||||||
|
, mChunkSize(ChunkSize)
|
||||||
|
, mBitPool(0)
|
||||||
|
, mBitsRemaining(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitStreamInWrapper::SetChunkSize(EChunkSize Size)
|
||||||
|
{
|
||||||
|
mChunkSize = Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
long CBitStreamInWrapper::ReadBits(long NumBits, bool ExtendSignBit /*= true*/)
|
||||||
|
{
|
||||||
|
long BitsRemaining = NumBits;
|
||||||
|
long Out = 0;
|
||||||
|
long Shift = 0;
|
||||||
|
|
||||||
|
while (BitsRemaining > 0)
|
||||||
|
{
|
||||||
|
if (mBitsRemaining <= BitsRemaining)
|
||||||
|
{
|
||||||
|
BitsRemaining -= mBitsRemaining;
|
||||||
|
Out |= (mBitPool << Shift);
|
||||||
|
Shift += mBitsRemaining;
|
||||||
|
ReplenishPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long Mask = (1 << BitsRemaining) - 1;
|
||||||
|
Out |= (mBitPool & Mask) << Shift;
|
||||||
|
mBitPool >>= BitsRemaining;
|
||||||
|
mBitsRemaining -= BitsRemaining;
|
||||||
|
BitsRemaining = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ExtendSignBit)
|
||||||
|
{
|
||||||
|
bool Sign = ((Out >> (NumBits - 1) & 0x1) == 1);
|
||||||
|
if (Sign) Out |= (-1 << NumBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBitStreamInWrapper::ReadBit()
|
||||||
|
{
|
||||||
|
return (ReadBits(1, false) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ************ PRIVATE ************
|
||||||
|
void CBitStreamInWrapper::ReplenishPool()
|
||||||
|
{
|
||||||
|
if (mChunkSize == e8Bit)
|
||||||
|
mBitPool = mpSourceStream->ReadByte();
|
||||||
|
else if (mChunkSize == e16Bit)
|
||||||
|
mBitPool = mpSourceStream->ReadShort();
|
||||||
|
else if (mChunkSize == e32Bit)
|
||||||
|
mBitPool = mpSourceStream->ReadLong();
|
||||||
|
|
||||||
|
mBitsRemaining = mChunkSize;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef CBITSTREAMINWRAPPER_H
|
||||||
|
#define CBITSTREAMINWRAPPER_H
|
||||||
|
|
||||||
|
#include "IInputStream.h"
|
||||||
|
|
||||||
|
class CBitStreamInWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum EChunkSize
|
||||||
|
{
|
||||||
|
e8Bit = 8, e16Bit = 16, e32Bit = 32
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
IInputStream *mpSourceStream;
|
||||||
|
EChunkSize mChunkSize;
|
||||||
|
unsigned long mBitPool;
|
||||||
|
long mBitsRemaining;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CBitStreamInWrapper(IInputStream *pStream, EChunkSize ChunkSize = e32Bit);
|
||||||
|
void SetChunkSize(EChunkSize Size);
|
||||||
|
long ReadBits(long NumBits, bool ExtendSignBit = true);
|
||||||
|
bool ReadBit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReplenishPool();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CBITSTREAMINWRAPPER_H
|
|
@ -14,4 +14,6 @@
|
||||||
#include "CVectorOutStream.h"
|
#include "CVectorOutStream.h"
|
||||||
#include "CTextOutStream.h"
|
#include "CTextOutStream.h"
|
||||||
|
|
||||||
|
#include "CBitStreamInWrapper.h"
|
||||||
|
|
||||||
#endif // FILEIO
|
#endif // FILEIO
|
||||||
|
|
Loading…
Reference in New Issue