///////////////////////////////////////////////////////////////////////////////
//
// Name:		SkeletonAnimDef.cpp
// Author:		Rich Cross
// Contact:		rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////

#include "precompiled.h"

#include "SkeletonAnimDef.h"
#include "ps/FilePacker.h"
#include "ps/FileUnpacker.h"


///////////////////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimDef constructor
CSkeletonAnimDef::CSkeletonAnimDef() : m_FrameTime(0), m_NumKeys(0), m_NumFrames(0), m_Keys(0)
{
}

///////////////////////////////////////////////////////////////////////////////////////////
// CSkeletonAnimDef destructor
CSkeletonAnimDef::~CSkeletonAnimDef() 
{
	delete[] m_Keys;
}

///////////////////////////////////////////////////////////////////////////////////////////
// BuildBoneMatrices: build matrices for all bones at the given time (in MS) in this 
// animation
void CSkeletonAnimDef::BuildBoneMatrices(float time,CMatrix3D* matrices) const
{
	float fstartframe=time/m_FrameTime;
	u32 startframe=u32(time/m_FrameTime);
	float deltatime=fstartframe-startframe;

	startframe%=m_NumFrames; 

	u32 endframe=startframe+1;
	endframe%=m_NumFrames; 

	u32 i;
	for (i=0;i<m_NumKeys;i++) {
		const Key& startkey=GetKey(startframe,i);
		const Key& endkey=GetKey(endframe,i);
		
		CVector3D trans=startkey.m_Translation*(1-deltatime)+endkey.m_Translation*deltatime;
		CQuaternion rot;
		rot.Slerp(startkey.m_Rotation,endkey.m_Rotation,deltatime);

		matrices[i].SetIdentity();
		matrices[i].Rotate(rot);
		matrices[i].Translate(trans);
	}
}

///////////////////////////////////////////////////////////////////////////////////////////
// Load: try to load the anim from given file; return a new anim if successful
CSkeletonAnimDef* CSkeletonAnimDef::Load(const char* filename)
{
	CFileUnpacker unpacker;
	unpacker.Read(filename,"PSSA");
			
	// check version
	if (unpacker.GetVersion()<FILE_READ_VERSION) {
		throw PSERROR_File_InvalidVersion();
	}

	// unpack the data
	CSkeletonAnimDef* anim=new CSkeletonAnimDef;
	try {
		CStr name; // unused - just here to maintain compatibility with the animation files
		unpacker.UnpackString(name);
		unpacker.UnpackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));
		unpacker.UnpackRaw(&anim->m_NumKeys,sizeof(anim->m_NumKeys));
		unpacker.UnpackRaw(&anim->m_NumFrames,sizeof(anim->m_NumFrames));
		anim->m_Keys=new Key[anim->m_NumKeys*anim->m_NumFrames];
		unpacker.UnpackRaw(anim->m_Keys,anim->m_NumKeys*anim->m_NumFrames*sizeof(Key));
	} catch (PSERROR_File&) {
		delete anim;
		throw;
	}

	return anim;
}

///////////////////////////////////////////////////////////////////////////////////////////
// Save: try to save anim to file
void CSkeletonAnimDef::Save(const char* filename,const CSkeletonAnimDef* anim)
{
	CFilePacker packer(FILE_VERSION, "PSSA");

	// pack up all the data
	packer.PackString("");
	packer.PackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));
	packer.PackRaw(&anim->m_NumKeys,sizeof(anim->m_NumKeys));
	packer.PackRaw(&anim->m_NumFrames,sizeof(anim->m_NumFrames));
	packer.PackRaw(anim->m_Keys,anim->m_NumKeys*anim->m_NumFrames*sizeof(Key));

	// now write it
	packer.Write(filename);
}

