Added support for loading compressed animations

This commit is contained in:
parax0 2016-04-08 09:02:15 -06:00
parent ef523c2339
commit a931e2aec6
6 changed files with 324 additions and 10 deletions

View File

@ -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();

View File

@ -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;
} }

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -14,4 +14,6 @@
#include "CVectorOutStream.h" #include "CVectorOutStream.h"
#include "CTextOutStream.h" #include "CTextOutStream.h"
#include "CBitStreamInWrapper.h"
#endif // FILEIO #endif // FILEIO