#include "StdAfx.h"

#include "AnimationLoader.h"
#include "CGF\LoaderCAF.h"
#include "CompressionController.h"
#include "AnimationManager.h"
#include "TrackStorage.h"
#include "crc32.h"
#include "AnimationCompiler.h"

CAnimation::CAnimation(CSkeletonInfo * pInfo) : m_pInfo(pInfo), 	m_bNewFormat(false),
m_bOldFormat(false), m_bTCBFormat(false)
{

}

CAnimation::~CAnimation(void)
{
	// smart pointers now
}


bool CAnimation::HasNewFormat()
{
	return m_bNewFormat;
}

bool CAnimation::LoadAnimationFromFile(const char * name, const char * savename, ILoaderCGFListener * pListener, SAnimationDesc& animDesc, bool bUseGLobal, bool bNeedCalculate)
{
	CLoaderCAF cgfLoader;

	m_sName = savename;
	FixString(m_sName);


	if (bNeedCalculate)
	{
		cgfLoader.m_bLoadOldChunk = true;
	}

	CInternalSkinningInfo* pSkinningInfo = cgfLoader.LoadCAF(name,&m_ChunkFile, 0);

	if (!pSkinningInfo)
	{
		LogError( "Failed to load animation file %s - %s",(const char*)name,cgfLoader.GetLastError() );
		return false;
	}

	m_bNewFormat = pSkinningInfo->m_bNewFormat;
	m_bOldFormat = pSkinningInfo->m_bOldFormat;
	m_bTCBFormat = pSkinningInfo->m_bTCBFormat;
	//----------------------------------------------------------------------------------------------

	GlobalAnimationHeader& rGlobalAnim = m_GlobalAnimationHeader;


	rGlobalAnim.m_nTicksPerFrame	= TICKS_PER_FRAME;
	rGlobalAnim.m_fSecsPerTick		= SECONDS_PER_TICK;
	rGlobalAnim.m_nStartKey				= pSkinningInfo->m_nStart;
	rGlobalAnim.m_nEndKey					= pSkinningInfo->m_nEnd;
	//	assert(rGlobalAnim.m_nTicksPerFrame==0xa0);

	int32 fTicksPerFrame  = rGlobalAnim.m_nTicksPerFrame;
	f32		fSecsPerTick		= rGlobalAnim.m_fSecsPerTick;
	f32		fSecsPerFrame		= fSecsPerTick * fTicksPerFrame;
	rGlobalAnim.m_fStartSec = rGlobalAnim.m_nStartKey * fSecsPerFrame;
	rGlobalAnim.m_fEndSec  = rGlobalAnim.m_nEndKey * fSecsPerFrame;
	if(rGlobalAnim.m_fEndSec<=rGlobalAnim.m_fStartSec)
		rGlobalAnim.m_fEndSec  = rGlobalAnim.m_fStartSec+(1.0f/30.0f);
	assert(rGlobalAnim.m_fStartSec>=0);
	assert(rGlobalAnim.m_fEndSec>=0);



	rGlobalAnim.m_fSpeed		= pSkinningInfo->m_Speed;
	rGlobalAnim.m_fDistance = pSkinningInfo->m_Distance;
	rGlobalAnim.m_fSlope		= pSkinningInfo->m_Slope;

	rGlobalAnim.m_FootPlantVectors.m_LHeelEnd		= pSkinningInfo->m_LHeelEnd;
	rGlobalAnim.m_FootPlantVectors.m_LHeelStart = pSkinningInfo->m_LHeelStart;
	rGlobalAnim.m_FootPlantVectors.m_LToe0End		= pSkinningInfo->m_LToe0End;
	rGlobalAnim.m_FootPlantVectors.m_LToe0Start = pSkinningInfo->m_LToe0Start;

	rGlobalAnim.m_FootPlantVectors.m_RHeelEnd		= pSkinningInfo->m_RHeelEnd;
	rGlobalAnim.m_FootPlantVectors.m_RHeelStart = pSkinningInfo->m_RHeelStart;
	rGlobalAnim.m_FootPlantVectors.m_RToe0End		= pSkinningInfo->m_RToe0End;
	rGlobalAnim.m_FootPlantVectors.m_RToe0Start = pSkinningInfo->m_RToe0Start;

	rGlobalAnim.m_MoveDirection = pSkinningInfo->m_MoveDirection;
	rGlobalAnim.m_StartPosition = pSkinningInfo->m_StartPosition;

	if (pSkinningInfo->m_Looped)
		rGlobalAnim.OnAssetCycle();

	rGlobalAnim.m_FootPlantBits.assign(pSkinningInfo->m_FootPlantBits.begin(), pSkinningInfo->m_FootPlantBits.end());


	uint32 numController = pSkinningInfo->m_arrControllerId.size();
	rGlobalAnim.m_arrController.resize(numController);

	m_LastGoodAnimation = m_CompressedAnimation = m_GlobalAnimationHeader;

	m_GlobalAnimationHeader.m_bDeleteControllers = true;
  m_GlobalAnimationHeader.m_arrBoneNameTable = pSkinningInfo->m_arrBoneNameTable;	//names of bones
	for(uint32 i=0; i<numController; i++ )
	{
		rGlobalAnim.m_arrController[i] = (IController*)pSkinningInfo->m_pControllers[i];//pController;
		rGlobalAnim.m_arrController[i]->m_pGlobalAnimationHeader = &rGlobalAnim;
	}

	if (bUseGLobal)
	{
		static int volatile g_AddHeader;
		WriteLock lock(g_AddHeader);

		CAnimationManager::GetInst().m_arrGlobalAnimations.push_back(m_GlobalAnimationHeader);//[0] = m_GlobalAnimationHeader;
		GAID = CAnimationManager::GetInst().m_arrGlobalAnimations.size() - 1;
		m_AnimDesc = animDesc;
	}

	return true;
}


uint32 CAnimation::SaveAnimationToFile( const char * name, ILoaderCGFListener * pListener, const ConvertContext &cc, bool bSaveInfo)
{

	//!

	//DeleteOldChunk(); 

	CSaverCGF cgfSaver( name, m_ChunkFile);

	//save information here
	SFileVersion fv = cc.pRC->GetFileVersion();

	//if (pCGF.get()) 
	//{

	//	pCGF->GetExportInfo()->rc_version[0] = fv.v[0];
	//	pCGF->GetExportInfo()->rc_version[1] = fv.v[1];
	//	pCGF->GetExportInfo()->rc_version[2] = fv.v[2];
	//	pCGF->GetExportInfo()->rc_version[3] = fv.v[3];
	//	sprintf( pCGF->GetExportInfo()->rc_version_string," RCVer:%d.%d ",fv.v[2],fv.v[1] );
	//	cgfSaver.SetContent( pCGF.get() );
	//	// Only store 
	//	cgfSaver.SaveExportFlags();
	//}
	//cgfSaver.SaveNodes();

	if (bSaveInfo)
	{
		std::auto_ptr<char> mem;

		struct SPEED_INFO {
			f32 Speed;
			f32 Distance;
			f32 Slope;
			int m_Looped;
			f32 m_MoveDir[3];
			QuatT m_StartDir;
		} speed;

		speed.Speed = m_GlobalAnimationHeader.m_fSpeed;
		speed.Distance = m_GlobalAnimationHeader.m_fDistance;
		speed.Slope= m_GlobalAnimationHeader.m_fSlope;
		speed.m_Looped = m_GlobalAnimationHeader.IsAssetCycle();
		speed.m_MoveDir[0] = m_GlobalAnimationHeader.m_MoveDirection.x;
		speed.m_MoveDir[1] = m_GlobalAnimationHeader.m_MoveDirection.y;
		speed.m_MoveDir[2] = m_GlobalAnimationHeader.m_MoveDirection.z;

		speed.m_StartDir = m_GlobalAnimationHeader.m_StartPosition;

		cgfSaver.SaveSpeedInfo2(&speed, sizeof(SPEED_INFO));
		//		cgfSaver.SaveSpeedInfo(&speed, sizeof(SPEED_INFO));

		if (m_AnimDesc.m_FootPlant)
		{
			struct FOOT_PLANT_INFO
			{
				int nPoses;

				f32 m_LHeelStart,m_LHeelEnd;
				f32 m_LToe0Start,m_LToe0End;
				f32 m_RHeelStart,m_RHeelEnd;
				f32 m_RToe0Start,m_RToe0End;
			} footplant;

			footplant.nPoses = m_GlobalAnimationHeader.m_FootPlantBits.size();

			footplant.m_LHeelEnd = m_GlobalAnimationHeader.m_FootPlantVectors.m_LHeelEnd;
			footplant.m_LHeelStart = m_GlobalAnimationHeader.m_FootPlantVectors.m_LHeelStart;
			footplant.m_LToe0Start = m_GlobalAnimationHeader.m_FootPlantVectors.m_LToe0Start;
			footplant.m_LToe0End = m_GlobalAnimationHeader.m_FootPlantVectors.m_LToe0End;

			footplant.m_RHeelEnd = m_GlobalAnimationHeader.m_FootPlantVectors.m_RHeelEnd;
			footplant.m_RHeelStart = m_GlobalAnimationHeader.m_FootPlantVectors.m_RHeelStart;
			footplant.m_RToe0Start = m_GlobalAnimationHeader.m_FootPlantVectors.m_RToe0Start;
			footplant.m_RToe0End = m_GlobalAnimationHeader.m_FootPlantVectors.m_RToe0End;

			int memsize = sizeof(FOOT_PLANT_INFO) + sizeof(uint8)*m_GlobalAnimationHeader.m_FootPlantBits.size();
			mem.reset(new char[memsize]);
			memcpy(mem.get(), &footplant, sizeof(FOOT_PLANT_INFO));
			memcpy((char*)(mem.get()) + sizeof(FOOT_PLANT_INFO),&m_GlobalAnimationHeader.m_FootPlantBits[0], sizeof(uint8)*m_GlobalAnimationHeader.m_FootPlantBits.size() );
			cgfSaver.SaveFootPlantInfo(mem.get(), memsize );
			// Force remove of the read only flag.


		}
		//	delete pCGF;
		SaveControllers(cgfSaver, m_GlobalAnimationHeader.m_arrController);
	}




	SetFileAttributes( name,FILE_ATTRIBUTE_ARCHIVE );
	m_ChunkFile.Write( name );

	GlobalAnimationHeader zero;

//	CAnimationManager::GetInst().m_arrGlobalAnimations[0] = zero;

	return m_ChunkFile.GetFileSize();
}


void CAnimation::MakeErrors(uint32 numBones, CompressionInfo &compInfo, int16 master, int16 footskel, SAnimationDesc * pDesc, CSkeletonInfo * pSkeleton, int weapon_bone)
{

	compInfo.m_Info.clear();

	if (m_AnimDesc.m_CompressionQuality == 0)
	{
		for (uint32 i=0; i<numBones; i++)
		{
			CompressionLevelInfo info;
			info.boneLevel = 0; //not used yet
			info.m_bPosCompression = eNoCompression;//eCompression;
			info.m_bRotCompression = eNoCompression;// eCompression;
			info.m_bSclCompression = eNoCompression;
			info.m_DeletePos = m_AnimDesc.m_DeletePosController;
			info.m_DeleteRot = m_AnimDesc.m_DeleteRotController;

			info.m_PositionError = 0.00000001f;
			info.m_RotationError = 0.00000001f;
			info.m_ScaleError = 0.00000001f;

			info.m_ScaleFormat = eNoCompressVec3;
			info.m_PositionFormat = eNoCompressVec3;
			info.m_RotationFormat = eSmallTree64BitExtQuat;//eSmallTree64BitQuat;//eNoCompressQuat;

			compInfo.m_Info.push_back(info);

		}

	}
	else
	{

		// trying to use preset info
		if (pSkeleton && pDesc->m_Preset.m_BoneInfoMap.size() > 0)
		{

			uint32 bones = pSkeleton->m_SkinningInfo.m_arrBonesDesc.size();

			for (uint32 i=0; i<numBones; i++)
			{

				uint32 currController = pSkeleton->m_SkinningInfo.m_arrBonesDesc[i].m_nControllerID;

				const CompressionPreset::BonePreset * pCurrBone = pDesc->m_Preset.FindBonePreset(currController);
				if (pCurrBone)
				{
					// use information from preset

					CompressionLevelInfo info;
					info.boneLevel = 0; //not used yet
					info.m_bPosCompression = eCompression;
					info.m_bRotCompression = eCompression;
					info.m_DeletePos = m_AnimDesc.m_DeletePosController;
					info.m_DeleteRot = m_AnimDesc.m_DeleteRotController;

					info.m_PositionError = pCurrBone->m_fPosError;
					info.m_RotationError = pCurrBone->m_fRotError;

					info.m_PositionFormat = pCurrBone->m_PosCompressionFormat;
					info.m_RotationFormat = pCurrBone->m_RotCompressionFormat;

					compInfo.m_Info.push_back(info);

				}
				else
				{
					DefaultError(master, compInfo, i, footskel);
				}

			}

		}
		else
		{
			for (uint32 i=0; i<numBones; i++)
			{
				DefaultError(master, compInfo, i, footskel);
			}
		}

	}

	// weapon_bone hack
	if (weapon_bone >=0 && weapon_bone < numBones) {
		compInfo.m_Info[weapon_bone].m_DeletePos = 0;
		compInfo.m_Info[weapon_bone].m_DeleteRot = 0;
	}
}

void CAnimation::ReverseTracks()
{
	//	std::vector<int> Times;

	GlobalAnimationHeader & header = m_GlobalAnimationHeader;

	for (uint32 c = 0; c < header.m_arrController.size(); ++c)
	{
		CControllerPQLog * pController = dynamic_cast<CControllerPQLog *>(header.m_arrController[c].get());
		if (pController)
		{
			uint32 countTimes = pController->m_arrTimes.size();
			for (uint32 i = 0; i < (uint32)(countTimes / 2); ++i)
			{
				uint32 newtime = countTimes-i-1;
				//Times[countTimes - i - 1] = pController->m_arrTimes[i];
				Vec3 q = pController->m_arrKeys[i].vRotLog;
				pController->m_arrKeys[i].vRotLog = pController->m_arrKeys[newtime].vRotLog;
				pController->m_arrKeys[newtime].vRotLog = q;


				Vec3 pos = pController->m_arrKeys[i].vPos;
				pController->m_arrKeys[i].vPos = pController->m_arrKeys[newtime].vPos;
				pController->m_arrKeys[newtime].vPos = pos;
			}
			//for (uint32 i = 0; i < countTimes; ++i)
			//{
			//	pController->m_arrTimes[i] = Times[i];
			//}
		}
		else
		{
			CController * pCController  = dynamic_cast<CController*>(header.m_arrController[c].get());

			if (!pCController)
			{
				assert(false);
				return;
			}

			uint32 countTimes = pCController->GetRotationController()->GetNumKeys();
			for (uint32 i = 0; i < (uint32)(countTimes / 2); ++i)
			{
				uint32 newtime = countTimes-i-1;
				//				Times[i] = pCController->GetRotationController()->GetTimeFromKey(countTimes-i-1);//pController->m_arrTimes[i];
				Quat q1, q2;
				pCController->GetRotationController()->GetValueFromKey(i, q1);
				pCController->GetRotationController()->GetValueFromKey(newtime, q2);
				pCController->GetRotationController()->GetRotationStorage()->SetValueForKey(i, q2);
				pCController->GetRotationController()->GetRotationStorage()->SetValueForKey(newtime, q1);

				Vec3 p1, p2;
				pCController->GetPositionController()->GetValueFromKey(i, p1);
				pCController->GetPositionController()->GetValueFromKey(newtime, p2);
				pCController->GetPositionController()->GetPositionStorage()->SetValueForKey(i, p2);
				pCController->GetPositionController()->GetPositionStorage()->SetValueForKey(newtime, p1);
			}
		}
	}
}



// Modified version of AnalyseAndModifyAnimations function
bool CAnimation::ProcessAnimation(ILoaderCGFListener * pListener, CTrackStorage * pStorage, bool bNeedCalculate, bool bUpdate)
{


	if (bNeedCalculate)
	{

		int16 lheel_idx = -1;
		int16 rheel_idx = -1;
		int16 ltoe_idx = -1;
		int16 rtoe_idx = -1;
		int16 ltoenub_idx = -1;
		int16 rtoenub_idx = -1;
		int16 master = -1;
		int16 weaponbone = -1;

		uint32 numBones(0);

		if (m_pInfo)
		{
			numBones=  m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();
		}

		CCompressonator compController;
		CompressionInfo compInfo;

		for (uint32 i=0; i<numBones; i++)
		{
			const char *  BoneName =  m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName;
			if (0 == stricmp(BoneName,"Bip01 L Heel"))
				lheel_idx=i;
			if (0 == stricmp(BoneName,"Bip01 L Toe0"))
				ltoe_idx=i;
			if (0 == stricmp(BoneName,"Bip01 L Toe0Nub"))
				ltoenub_idx=i;

			if (0 == stricmp(BoneName,"Bip01 R Heel"))
				rheel_idx=i;
			if (0 == stricmp(BoneName,"Bip01 R Toe0"))
				rtoe_idx=i;
			if (0 == stricmp(BoneName,"Bip01 R Toe0Nub"))
				rtoenub_idx=i;
			if (0 == stricmp(BoneName,"MASTER"))
				master=i;
			if (0 == stricmp(BoneName,"weapon_bone"))
				weaponbone=i;


			//fill compressinfo
		}

		uint32 footskel=0;
		if (lheel_idx>=0 && rheel_idx>=0  && ltoe_idx>=0 && rtoe_idx>=0 && ltoenub_idx>=0 && rtoenub_idx>=0)
			footskel=1;

		bool bFootplant(false);

		std::vector< std::vector<DebugJoint> > g_arrSkeletons;

		CAnimationManager::GetInst().m_arrGlobalAnimations[GAID] = m_GlobalAnimationHeader;

		if (m_AnimDesc.m_ReverseDirection)
		{
			ReverseTracks();
		}

		if ( footskel)//m_AnimDesc.m_Human)
		{

			const char * animname = m_sName.c_str();

			CryBoneDescData* pModelJoint = &m_pInfo->m_SkinningInfo.m_arrBonesDesc[0];//&m_pModel->m_arrModelJoints[0];

			Matrix34 abs_root	 = pModelJoint[0].m_DefaultW2B;
			Matrix34 abs_pelvis = pModelJoint[1].m_DefaultW2B;
			Vec3 pos = abs_root.GetTranslation();
			pos.z = 0;
			abs_root.SetTranslation(pos);
			//abs_root.t.z=0;

			Matrix34 rel_root	 = abs_root;
			Matrix34 rel_pelvis = abs_root.GetInverted()*abs_pelvis;


			pModelJoint[0].m_DefaultW2B = abs_root;
			pModelJoint[0].m_DefaultB2W = rel_root;
			pModelJoint[1].m_DefaultB2W = rel_pelvis;



			DetectCycleAnimations(pListener);
			BigFuckingArrow(lheel_idx,rheel_idx, m_AnimDesc.m_RootToHeels != 0);
			EvaluateSpeed(pListener);

			bFootplant = true;

			if (m_AnimDesc.m_FootPlant)
			{
				SetFootplantBitsAutomatically( g_arrSkeletons,  0 , 0, lheel_idx,rheel_idx,ltoe_idx,rtoe_idx,ltoenub_idx,rtoenub_idx, m_AnimDesc);

				if (m_GlobalAnimationHeader.IsAssetCycle())
				{
					if (m_GlobalAnimationHeader.m_fDistance>0.2f)
					{
						SFootPlant& rFootPlantVectors = m_GlobalAnimationHeader.m_FootPlantVectors;
						SetFootplantVectors(	g_arrSkeletons,/*i*/0, rFootPlantVectors,/*GlobalID*/ 0 );
					}
				}

			}

		}
		else
		{ 
			if (numBones)
			{
				DetectCycleAnimations(pListener);
				EvaluateSpeed(pListener);
			}
			else
			{
				// simple pass for default params
				numBones = m_GlobalAnimationHeader.m_arrController.size();

			}
		}


		// Reverse


		int fails = 3;

		m_GlobalAnimationHeader.m_nTicksPerFrame	= TICKS_PER_FRAME;	//pSkinningInfo->m_nTicksPerFrame;
		m_GlobalAnimationHeader.m_fSecsPerTick		= SECONDS_PER_TICK;	//pSkinningInfo->m_secsPerTick;


		if (m_AnimDesc.m_FixedToFoots && bFootplant && footskel && m_AnimDesc.m_FootPlant && m_AnimDesc.m_CompressionQuality)
		{

			bool q = true;
			bool res = false;
			float coeff = 1.0f;
			float step = 10.0f;

			if (m_GlobalAnimationHeader.m_arrController.size() > numBones)
			{
				numBones = m_GlobalAnimationHeader.m_arrController.size();
			}

			while(q)
			{


				compInfo.m_Info.clear();

				MakeCompression(numBones, coeff, compInfo, compController, eSmallTree64BitExtQuat, footskel);

				std::vector< std::vector<DebugJoint> > tmpSkeletons;
				CreateSkeletonArray( tmpSkeletons, m_CompressedAnimation);

				// calculate differences between compressed and not compressed
				bool p1 = GetError(tmpSkeletons, g_arrSkeletons, lheel_idx, 0.001f * (float)m_AnimDesc.m_CompressionQuality);
				bool p2 = GetError(tmpSkeletons, g_arrSkeletons, rheel_idx, 0.001f * (float)m_AnimDesc.m_CompressionQuality);

				if (p1 && p2)
				{
					//try decrease errors
					coeff *= step;
					m_LastGoodAnimation = m_CompressedAnimation;
					res = true;
					if (0.00000001f * coeff > 1.0f)
					{
						// maybe aim pose?
						q = false;
						MakeErrors(numBones, compInfo, master, footskel, &m_AnimDesc, m_pInfo, weaponbone);
						compController.CreateCompression(m_GlobalAnimationHeader, m_GlobalAnimationHeader,  compInfo, m_pInfo, m_AnimDesc, GAID);
					}

				}
				else
				{
					if (fails-- < 0)
					{
						q = false;
						// Rollback to the previuos
						if (res)
						{

							MakeCompression(numBones, coeff, compInfo, compController, eAutomaticQuat, footskel);
							m_GlobalAnimationHeader.m_arrController = m_LastGoodAnimation.m_arrController;
						}
						else
						{
							q = false;

							MakeErrors(numBones, compInfo, master, footskel, &m_AnimDesc, m_pInfo, weaponbone);
							compController.CreateCompression(m_GlobalAnimationHeader, m_GlobalAnimationHeader,  compInfo, m_pInfo, m_AnimDesc, GAID);
						}

					}

					step = 1.3f;
					coeff = coeff / 2.0f;								
				}

			}
		}
		else
		{
			if (m_GlobalAnimationHeader.m_arrController.size() > numBones)
			{
				numBones = m_GlobalAnimationHeader.m_arrController.size();
			}

			MakeErrors(numBones, compInfo, master, footskel, &m_AnimDesc, m_pInfo, weaponbone);
			{

				// need fixup for the hunter animation. problems with loss precision 
				// !!!!!!!!!!!!!! OLD CONVERSION !!!!!!!!!!!!!!!!!!!
				compController.CreateCompression(m_GlobalAnimationHeader, m_GlobalAnimationHeader,  compInfo, m_pInfo, m_AnimDesc, GAID);
			}
		}
	}

	//SaveToDB(pStorage, bUpdate);
	return true;
}

void CAnimation::SaveToDB(CTrackStorage * pStorage, bool bUpdate)
{

	static volatile int g_lockMemDB;
	WriteLock lock(g_lockMemDB);

	if (pStorage)
	{
		if (bUpdate)
			pStorage->UpdateAnimation(m_GlobalAnimationHeader, m_sName, true, m_dwTimestamp, false);
		else
			pStorage->AddAnimation(m_GlobalAnimationHeader, m_sName, true, m_dwTimestamp);
	}
//	lock.Unlock();
}


void CAnimation::DetectCycleAnimations(ILoaderCGFListener * pListener)//uint32 AnimID)
{

	uint32 numJoints = m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();

	bool equal=1;
	for (uint32 j=0; j<numJoints; j++)
	{
		if (m_pInfo->m_SkinningInfo.m_arrBonesDesc[j].m_nOffsetParent == 0)
			continue;

		int AnimID = 0;
		IController* pController = m_GlobalAnimationHeader.GetController(m_pInfo->m_SkinningInfo.m_arrBonesDesc[j].m_nControllerID);;//m_GlobalAnimationHeader.m_arrController[AnimID];//pModelJoint[j].m_arrControllersMJoint[AnimID];
		if (pController==0)
			continue;

		int GlobalAnimationID = GAID;
		Quat SRot;	pController->GetO(GAID, 	0, SRot );
		Quat ERot	=	pController->GetControllerInfo().quat;

		Ang3 sang=Ang3(SRot);
		Ang3 eang=Ang3(ERot);

		CInfo info	=	pController->GetControllerInfo();

		bool status  = SRot.IsEquivalent(ERot,0.1f);

		equal &= status;
	}

	if (equal)
		m_GlobalAnimationHeader.OnAssetCycle();
	else
		m_GlobalAnimationHeader.OnAssetNoCycle();

}



void CAnimation::EvaluateSpeed(ILoaderCGFListener * pListener)//uint32 AnimID)
{

	uint32 TicksPerSecond = TICKS_PER_SECOND;
	f32 fStart		=	m_GlobalAnimationHeader.m_fStartSec;
	f32 fDuration	=	m_GlobalAnimationHeader.m_fEndSec - m_GlobalAnimationHeader.m_fStartSec;
	f32 fDistance	=	0.0f;

	int bip = -1;

	uint32 numBones =  m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();
	for (uint32 i=0; i<numBones; i++)
	{
		const char *  BoneName =  m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName;
		if (0 == stricmp(BoneName,"Bip01"))
			bip=i;
	}

	if (bip == -1)
	{
		for (uint32 i=0; i<numBones; i++)
		{
			const char *  BoneName =  m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName;
			if (0 == stricmp(BoneName,"MASTER"))
				bip=i;
		}
	}

	if (bip == -1)
	{

		m_GlobalAnimationHeader.m_fDistance = 0.0f;
		m_GlobalAnimationHeader.m_fSpeed  = 0.0f;
		return;
	}


	IController * pController = m_GlobalAnimationHeader.GetController(m_pInfo->m_SkinningInfo.m_arrBonesDesc[bip].m_nControllerID);//m_arrController[bip];
	//distance of this animation (we use it to calculate the speed)
	int GlobalAnimationID = 0;

	if (pController)
	{

		Vec3 SPos0,SPos1;	
		f32 newtime = 0; //fStart*TicksPerSecond;
		pController->GetP( GAID, newtime, SPos0 );
		for (f32 t=0.01f; t<=1.0f; t=t+0.01f)
		{
			//newtime = fStart*TicksPerSecond + t*TicksPerSecond*fDuration;
			newtime = t;
			pController->GetP( GAID, newtime, SPos1 );
			if (SPos0 != SPos1 )
			{
				fDistance += (SPos0-SPos1).GetLength();
				SPos0=SPos1;
			}
			else
			{
				int a  = 0;
			}
		}

	}

	m_GlobalAnimationHeader.m_fDistance			=	fDistance;
	m_GlobalAnimationHeader.m_fSpeed        = fDistance/fDuration;

	if (fDuration<0.001f)	
	{
		//		assert(0);
		LogWarning ("CryAnimation: Animation-asset '%s' has just one keyframe. Impossible to detect Duration", m_GlobalAnimationHeader.GetPathName());
	}

}

ILINE f32 GetYawFromQuat(const Quat& quat )
{
	Vec3 xaxis0 = Vec3(0,-1,0);
	Vec3 xaxis1 = quat.GetColumn0();  xaxis1.z=0;	xaxis1.Normalize();
	Vec3 cross  = xaxis0%xaxis1;
	f32 sign = (cross.z<0) ? -1.0f : 1.0f;
	return atan2f( sign*cross.GetLength(), xaxis0|xaxis1 );
}


#define LEFT  ( Vec3( 1.00f,-0.00f,0).GetNormalized() )
#define RIGHT ( Vec3(-1.00f,-0.00f,0).GetNormalized() )
#define BACK  ( Vec3( 0.00f, 1.00f,0).GetNormalized() )



void CAnimation::CreateNewController( std::vector<int>& arrFullTimes, std::vector<PQLog>& arrFullQTKeys, int numController, float fRotErr, float fPosErr) 
{
	CController * pNewController = new CController;

	//KeyTimesInformationPtr pTime;// = new F32KeyTimesInformation;//CKeyTimesInformation;

	//if (arrFullTimes[arrFullTimes.size()-1] /*/ 160*/  < 256)
	//	pTime = new ByteKeyTimesInformation;
	//else
	//	if (arrFullTimes[arrFullTimes.size()-1]/*/ 160*/ < 65536)
	//		pTime = new UINT16KeyTimesInformation;
	//	else
	//		pTime = new F32KeyTimesInformation;


	//TrackInformationPtr pRotations  =  TrackInformationPtr(new RotationTrackInformation);
	//TrackRotationStoragePtr pRotStorage = ControllerHelper::GetRotationControllerPtr(eSmallTree64BitExtQuat);
	//pRotations->SetRotationStorage(pRotStorage);


	//PositionInformationPtr pPositions = PositionInformationPtr(new PositionTrackInformation);
	//TrackPositionStoragePtr pPosStorage = ControllerHelper::GetPositionControllerPtr(0);
	//pPositions->SetPositionStorage(pPosStorage);


	//pPositions->SetKeyTimesInformation(pTime);
	//pRotations->SetKeyTimesInformation(pTime);

	uint32 i;

	CCompressonator compressonator;

	int32 oldtime =  -1;
	for ( i = 0; i < arrFullTimes.size(); ++i)
	{
		if (oldtime != arrFullTimes[i])
		{
			oldtime = arrFullTimes[i];

			compressonator.m_RotTimes.push_back(arrFullTimes[i]);
			compressonator.m_PosTimes.push_back(arrFullTimes[i]);
			Quat q = !exp(arrFullQTKeys[i].vRotLog);
			compressonator.m_Rotations.push_back(q);
			compressonator.m_Positions.push_back(arrFullQTKeys[i].vPos);
			//pTime->AddKeyTime( arrFullTimes[i]);
			//Quat q = !exp(arrFullQTKeys[i].vRotLog);
			//pRotStorage->AddValue(q);
			//pPosStorage->AddValue(arrFullQTKeys[i].vPos);
		}
	}

	CompressionLevelInfo info;
	info.m_RotationFormat = eSmallTree64BitExtQuat;
	info.m_RotationError = fRotErr;
	info.m_PositionError = fPosErr;
	info.m_PositionFormat = eNoCompressVec3;
	if (info.m_RotationError != 0.0f)
		info.m_bRotCompression = eCompression;
	else
		info.m_bRotCompression = eNoCompression;
	if (info.m_PositionError != 0.0f)
		info.m_bPosCompression = eCompression;
	else
		info.m_bPosCompression = eNoCompression;
	compressonator.CompressPositions(pNewController, &info, false);
	compressonator.CompressRotations(pNewController, &info, false);

	//pNewController->SetPositionController(PositionInformationPtr(pPositions));
	//pNewController->SetRotationController(TrackInformationPtr(pRotations));


	pNewController->m_nControllerId = m_GlobalAnimationHeader.m_arrController[numController]->GetID();
	pNewController->m_pGlobalAnimationHeader = &m_GlobalAnimationHeader;
	m_GlobalAnimationHeader.m_arrController[numController] = pNewController;



	//// STUFF FOR MATHEMATICAL TESTS
	//FILE * f = fopen("e:\\uncompquat.txt", "wb");
	//Vec3 pos;
	//
	//for (uint32 a = 0; a < pRotations->GetNumKeys(); ++a)
	//{
	//	f32 time = pRotations->GetKeyTimesInformation()->GetKeyValueFloat(a);
	//	
	//	Quat quats;
	//	pRotations->GetValueFromKey(a, quats);
	//	Ang3 ang;
	//	ang = Ang3::GetAnglesXYZ(quats);
	//	pPositions->GetValueFromKey(a, pos);
	//	fprintf(f, "%f %f %f %f %f %f %f\n", time, pos.x, pos.y, pos.z, ang.x, ang.y, ang.z);
	//}
	//fclose(f);
	//// END

}

bool CAnimation::CheckLoco(std::string& str)
{

	uint32 numJoints = m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();

	// searching BIP01 and Bip01Pelvis

	int Bip01 = -1;
	int Bip01Pelvis = -1;

	int LocomotionLocator = -1;


	//for (int i = 0; i < m_pInfo->m_SkinningInfo.m_arrBonesDesc.size(); ++i)
	//{
	//	if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "Bip01") == 0)
	//	{
	//		Bip01= i;
	//	}

	//	if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "locomotion_") == 0)
	//	{
	//		LocomotionLocator= i;
	//	}
	//}

	int bip = Crc32Gen::GetCRC32("Bip01");
	IController* pController0 = m_GlobalAnimationHeader.GetController(bip);
	int locator_locoman01 = Crc32Gen::GetCRC32("Locator_Locomotion");
	IController* pLocomotionController =  m_GlobalAnimationHeader.GetController(locator_locoman01);

	if (!pController0)
	{
		str = "Bip01 not exist";
		return true;
	}

	if (!pLocomotionController)
	{
		str = "Locator_Locomotion not exist";
			return true;
	}

	BigFuckingArrow(-1, -1, 0, true, true);
	BigFuckingArrow(-1, -1, 0, true, false);


	pController0 = m_GlobalAnimationHeader.m_arrController[2];
	IController* pController1 = m_GlobalAnimationHeader.m_arrController[3];

	std::vector<QuatT> arrFullQTKeys0;
	std::vector<QuatT> arrFullQTKeys1;

	
	GlobalAnimationHeader& rGlobalAnimHeader = m_GlobalAnimationHeader;
	
	CInfo ERot0	=	pController0->GetControllerInfo();


	arrFullQTKeys0.resize(ERot0.realkeys);

	int32 startkey	=	int32(rGlobalAnimHeader.m_fStartSec*TICKS_PER_SECOND);
	Diag33 Scale;

	int32 stime	=	startkey;
	for (int32 t=0; t<ERot0.realkeys; t++)
	{
		pController0->GetOPS(GAID, f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)),arrFullQTKeys0[t].q,arrFullQTKeys0[t].t,Scale);	

		//arrFullTimes0[t]=stime;
		//arrFullTimes1[t]=stime;
		stime += TICKS_PER_FRAME;
	}

	ERot0	=	pController1->GetControllerInfo();
	arrFullQTKeys1.resize(ERot0.realkeys);

	stime	=	startkey;

	for (int32 t=0; t<ERot0.realkeys; t++)
	{
		pController1->GetOPS(GAID, f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)),arrFullQTKeys1[t].q,arrFullQTKeys1[t].t,Scale);	

		stime += TICKS_PER_FRAME;
	}

		
	bool bFailed(false);

	if (!arrFullQTKeys0[0].IsEquivalent(arrFullQTKeys1[0]))
	{
		str = "First keys are different. ";
		bFailed = true;
	}

	if (!arrFullQTKeys0[arrFullQTKeys0.size() - 1].IsEquivalent(arrFullQTKeys1[arrFullQTKeys1.size() - 1]))
	{
		str += "Last keys are different.";
			bFailed = true;
	}

	return bFailed;
}


void CAnimation::BigFuckingArrow(int lheel_idx, int rheel_idx, bool bRootToHeels, bool bCheckLocoStuff, bool bUseLoco)
{

	uint32 numJoints = m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();

	// searching BIP01 and Bip01Pelvis

	int Bip01 = -1;
	int Bip01Pelvis = -1;

	int LocomotionLocator = -1;

	


/*
	for (int i = 0; i < m_pInfo->m_SkinningInfo.m_arrBonesDesc.size(); ++i)
	{
		if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "Bip01") == 0)
		{
			Bip01= i;
		}

		if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "Bip01 Pelvis") == 0)
		{
			Bip01Pelvis= i;
		}

		if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "locomotion_") == 0)
		{
			LocomotionLocator= i;
		}
	}
*/

	int Bip01ControllerID = Crc32Gen::GetCRC32("Bip01");;
	int Bip01PelvisControllerID = Crc32Gen::GetCRC32("Bip01 Pelvis");

	if (!bCheckLocoStuff)
	{
		for (int i = 0; i < m_pInfo->m_SkinningInfo.m_arrBonesDesc.size(); ++i)
		{
			if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "Bip01") == 0)
			{
				Bip01= i;
			}

			if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "Bip01 Pelvis") == 0)
			{
				Bip01Pelvis= i;
			}

			if (stricmp(m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_arrBoneName, "locomotion_") == 0)
			{
				LocomotionLocator= i;
			}
		}

		assert(m_pInfo->m_SkinningInfo.m_arrBonesDesc[Bip01].m_nControllerID == Bip01ControllerID);
		assert(m_pInfo->m_SkinningInfo.m_arrBonesDesc[Bip01Pelvis].m_nControllerID == Bip01PelvisControllerID);
	}


	IController* pController0 = m_GlobalAnimationHeader.GetController(Bip01ControllerID);
	IController* pController1 = m_GlobalAnimationHeader.GetController(Bip01PelvisControllerID);

	if (bCheckLocoStuff)
	{
		if (bUseLoco)
			Bip01 = 2;
		else
			Bip01 = 3;
	}
	
	int locator_locoman01 = Crc32Gen::GetCRC32("Locator_Locomotion");
	IController* pLocomotionController = bUseLoco ? m_GlobalAnimationHeader.GetController(locator_locoman01) : 0;
	//	IController* pControllerLHeel = m_GlobalAnimationHeader.GetController(m_pInfo->m_SkinningInfo.m_arrBonesDesc[lheel_idx].m_nControllerID);//m_GlobalAnimationHeader.m_arrController[Bip01Pelvis];//pModelJoint[1].m_arrControllersMJoint[AnimID];
	//	IController* pControllerRHeel = m_GlobalAnimationHeader.GetController(m_pInfo->m_SkinningInfo.m_arrBonesDesc[rheel_idx].m_nControllerID);//m_GlobalAnimationHeader.m_arrController[Bip01Pelvis];//pModelJoint[1].m_arrControllersMJoint[AnimID];

//	pLocomotionController = 0;

	std::vector< std::vector<DebugJoint> > tmpSkeletons;


	if (!bCheckLocoStuff)
		CreateSkeletonArray( tmpSkeletons, m_GlobalAnimationHeader);


	GlobalAnimationHeader& rGlobalAnimHeader = m_GlobalAnimationHeader;//CAnimationManager::GetInst().m_arrGlobalAnimations[GlobalAnimationID];

	int GlobalAnimationID = 0;

	//evaluate the distance and the duration of this animation (we use it to calculate the speed)
	if (pController0)
	{
		Diag33 Scale;

		CInfo ERot0	=	pController0->GetControllerInfo();
		if (ERot0.realkeys==1)
			return;

		std::vector<QuatT> arrFullQTKeys0;
		std::vector<int> arrFullTimes0;
		std::vector<PQLog> arrFullKeys0;

		std::vector<int> _arrFullTimes0;
		std::vector<PQLog> _arrFullKeys0;

		std::vector<QuatT> arrFullQTKeys1;
		std::vector<int> arrFullTimes1;
		std::vector<PQLog> arrFullKeys1;

		std::vector<uint8> arrUsedKeys;

		arrFullQTKeys0.resize(ERot0.realkeys);
		arrFullTimes0.resize(ERot0.realkeys);
		arrFullKeys0.resize(ERot0.realkeys);

		arrFullQTKeys1.resize(ERot0.realkeys);
		arrFullTimes1.resize(ERot0.realkeys);
		arrFullKeys1.resize(ERot0.realkeys);

		int32 startkey	=	int32(rGlobalAnimHeader.m_fStartSec*TICKS_PER_SECOND);
		int32 stime	=	startkey;
		for (int32 t=0; t<ERot0.realkeys; t++)
		{
			pController0->GetOPS(GAID, f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)),arrFullQTKeys0[t].q,arrFullQTKeys0[t].t,Scale);	
			pController1->GetOPS(GAID, f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)),arrFullQTKeys1[t].q,arrFullQTKeys1[t].t,Scale);	
			arrFullTimes0[t]=stime;
			arrFullTimes1[t]=stime;
			stime += TICKS_PER_FRAME;
		}

		Vec3 firstpos(0,0,0);

		QuatT tmpfirst;
		tmpfirst.SetIdentity();


		if (pLocomotionController)
		{
			pLocomotionController->GetOPS(GAID, 0,tmpfirst.q,firstpos,Scale);	

			rGlobalAnimHeader.m_StartPosition.q = tmpfirst.q;
			rGlobalAnimHeader.m_StartPosition.t = firstpos;

			//tmpfirst.t = firstpos;

			tmpfirst = tmpfirst.GetInverted();
			tmpfirst/*.q*/ = tmpfirst/*.q*/ * Quat::CreateRotationZ(-gf_PI - 0.001f);
		}
		else
		{
			pController0->GetOPS(GAID, 0,tmpfirst.q,firstpos,Scale);	


			rGlobalAnimHeader.m_StartPosition.q = tmpfirst.q;
			rGlobalAnimHeader.m_StartPosition.t = firstpos;
			//rGlobalAnimHeader.m_StartPosition.t = firstpos;
			firstpos.z = 0;
			//tmpfirst.t = firstpos;

			tmpfirst = tmpfirst.GetInverted();
			tmpfirst/*.q*/ = tmpfirst/*.q*/ * Quat::CreateRotationZ(-gf_PI/2.0f);

		}

		/*		Quat firstInv = tmpfirst.q.GetInverted();*/
		f32 xval=/*arrFullQTKeys0[0].t.x +*/ firstpos.x;
		f32 yval=/*arrFullQTKeys0[0].t.y +*/ firstpos.y;
		f32 zval = /*arrFullQTKeys0[0].t.z +*/ firstpos.z;
		//rGlobalAnimHeader.m_StartPos=Vec3(xval,yval,zval);

		if (pLocomotionController)
		{
			for (int32 t=0; t<ERot0.realkeys; t++)
			{
				arrFullQTKeys0[t].t.x -= xval;
				arrFullQTKeys0[t].t.y -= yval;
				arrFullQTKeys0[t].t.z -= zval;
			}

			for (int32 t=0; t<ERot0.realkeys; t++)
			{
				arrFullQTKeys0[t] = tmpfirst * arrFullQTKeys0[t];
				arrFullQTKeys1[t] = arrFullQTKeys0[t]*arrFullQTKeys1[t]; //absolute Pivot
				//arrFullQTKeys1[t] = arrFullQTKeys1[t] * Quat::CreateRotationZ(-gf_PI - 0.001f);
			}
		}
		else
		{
			for (int32 t=0; t<ERot0.realkeys; t++)
			{
				arrFullQTKeys0[t].t.x -= xval;
				arrFullQTKeys0[t].t.y -= yval;
				arrFullQTKeys0[t].t.z -= zval;
			}
			for (int32 t=0; t<ERot0.realkeys; t++)
			{
				//arrFullQTKeys0[t] = tmpfirst * arrFullQTKeys0[t];
				arrFullQTKeys1[t] = arrFullQTKeys0[t]*arrFullQTKeys1[t]; //absolute Pivot
				//arrFullQTKeys1[t] = arrFullQTKeys1[t] * Quat::CreateRotationZ(-gf_PI - 0.001f);
			}


		}

		Quat iquat=!arrFullQTKeys0[0].q*Quat::CreateRotationZ(-gf_PI/2);


		stime	=	startkey;
		for (int32 t=0; t<ERot0.realkeys; t++)
		{
			float rootPos = 0.0f;
			//if (bRootToHeels)
			//{
			//	int newtime = f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)) * 60.0f;//
			//	rootPos = (tmpSkeletons[newtime][lheel_idx].m_AbsoluteQuat.t.z + tmpSkeletons[newtime][rheel_idx].m_AbsoluteQuat.t.z)/2.0f;
			//}

			stime += TICKS_PER_FRAME;
//			arrFullQTKeys0[t].t.z  = rootPos;
			arrFullQTKeys0[t]	= arrFullQTKeys0[t]*iquat;

			f32 yaw=GetYawFromQuat(arrFullQTKeys0[t].q);
			arrFullQTKeys0[t].q=Quat::CreateRotationZ(yaw-gf_PI/2);
		}

		//-------------------------------------------------------------------------------------------
		//---  here is a list of animation where we need to adjust the height of the pelvis       ---          
		//-------------------------------------------------------------------------------------------
		uint32 HillAnim=0; 
		size_t PosInString;
		PosInString = m_sName.find("uphill");
		if (PosInString != string::npos)	HillAnim|=1;
		PosInString = m_sName.find("downhill");
		if (PosInString != string::npos)	HillAnim|=1;

		if (HillAnim)
		{

			//_asm 
			//{
			//	int 3;
			//}

			Vec3 p0 = arrFullQTKeys1[0].t;
			Vec3 p1 = arrFullQTKeys1[ERot0.realkeys-1].t;


			Vec3 v0	=	Vec3(0,-1,0);
			Vec3 v1	=	(p1-p0).GetNormalized();

			Vec3 cross  = v0%v1;
			f32 sign		= (cross.x<0) ? -1.0f : 1.0f;
			f32 rad			= atan2f( sign*cross.GetLength(), v0|v1 );
			f32 deg			= RAD2DEG(rad);

			f32 dif_z = p1.z-p0.z;	
			f32 dif_y = p1.y-p0.y;	
			rGlobalAnimHeader.m_fSlope=rad;

			for (int32 t=0; t<ERot0.realkeys; t++)
			{
				arrFullQTKeys0[t].q.SetRotationZ(IDENTITY); //SetSlerp(g_UphillStart[i], g_UphillEnd[i], f32(t)/f32(ERot0.realkeys-1) );
				arrFullQTKeys0[t].t.z=0;

				f32 percent = arrFullQTKeys0[t].t.y/dif_y;
				f32 newz = dif_z*percent;
				arrFullQTKeys1[t].t.z=arrFullQTKeys1[t].t.z-newz;
			}
		}


		//-------------------------------------------------------------------------------------------
		//---  here is a list of animation where we need to retain or modify the BODY-DIRECTION   ---          
		//-------------------------------------------------------------------------------------------
		//for (uint32 i=0; i<g_BodyDirCounter; i++)
		//{
		//	int32 gid=g_BodyDirGlobalID[i];
		//	if (gid>=0)
		//	{
		//		if (gid==pAnim0->m_nGlobalAnimId)
		//		{
		//			for (int32 t=0; t<ERot0.realkeys; t++)
		//				arrFullQTKeys0[t].q.SetSlerp(g_BodyDirStart[i], g_BodyDirEnd[i], f32(t)/f32(ERot0.realkeys-1) );
		//		}
		//	}
		//}

		if (m_AnimDesc.m_Directions.size() && !pLocomotionController)
		{

			Quat BodyDirStart;// = 
			Quat BodyDirEnd;// = 

			/*
			if (m_AnimDesc.m_Direction == ED_Identity)
			{
			BodyDirStart.SetRotationZ(IDENTITY);
			BodyDirEnd.SetRotationZ(IDENTITY);
			}
			else
			if (m_AnimDesc.m_Direction == ED_L90)
			{
			BodyDirStart.SetRotationZ(IDENTITY);
			BodyDirEnd.SetRotationZ(L90);
			}
			else
			if (m_AnimDesc.m_Direction == ED_R90)
			{
			BodyDirStart.SetRotationZ(IDENTITY);
			BodyDirEnd.SetRotationZ(R90);
			}
			*/

			/*
			BodyDirStart.SetRotationZ(IDENTITY);
			BodyDirEnd.SetRotationZ(IDENTITY - m_AnimDesc.m_fDirection);


			for (int32 t=0; t<ERot0.realkeys; t++)
			arrFullQTKeys0[t].q.SetSlerp(BodyDirStart, BodyDirEnd, f32(t)/f32(ERot0.realkeys-1) );
			*/

			int nextStep = 1;
			BodyDirStart.SetRotationZ(m_AnimDesc.m_Directions[0].m_fDirection);
			BodyDirEnd.SetRotationZ(m_AnimDesc.m_Directions[1].m_fDirection);
			float nextTime =  m_AnimDesc.m_Directions[1].m_fTime * f32(ERot0.realkeys-1);
			float prevTime = 0.0f;

			//			float relativeTime = 0.0f;
			//			float relativeStep = m_AnimDesc.m_Directions[1].m_fTime * ;

			for (int32 t=0; t<ERot0.realkeys; t++)
			{
				//				float totalTime = f32(t)/f32(ERot0.realkeys-1);

				if (t > nextTime)
				{
					// next time period
					BodyDirStart.SetRotationZ(m_AnimDesc.m_Directions[nextStep].m_fDirection);
					BodyDirEnd.SetRotationZ(m_AnimDesc.m_Directions[nextStep+1].m_fDirection);
					prevTime = nextTime;
					nextTime =  m_AnimDesc.m_Directions[nextStep+1].m_fTime* f32(ERot0.realkeys-1);

					nextStep++;
				}
				float relativeTime = (f32(t) - prevTime) / (nextTime - prevTime);

				arrFullQTKeys0[t].q.SetSlerp(BodyDirStart, BodyDirEnd, /*f32(t)/f32(ERot0.realkeys-1)*/ relativeTime );
			}
		}

		if (m_AnimDesc.m_MoveDirection != EMD_Ignore)
		{

			Vec3 MoveDirVec;

			if (m_AnimDesc.m_MoveDirection == eMD_Back)
				MoveDirVec = BACK;
			else
				if (m_AnimDesc.m_MoveDirection == eMD_Left)
					MoveDirVec = LEFT;
				else
					if (m_AnimDesc.m_MoveDirection == eMD_Right)
						MoveDirVec = RIGHT;

			for (int32 t=0; t<ERot0.realkeys; t++)
				m_GlobalAnimationHeader.m_MoveDirection=MoveDirVec;


		}



		// we have a root controller
		if (pLocomotionController)
		{
			CInfo info	=	pLocomotionController->GetControllerInfo();
//			arrFullQTKeys0.resize(info.realkeys);
			arrFullTimes0.resize(info.realkeys);

			_arrFullKeys0.clear();
			PQLog pqlog;

			int32 startkey	=	int32(rGlobalAnimHeader.m_fStartSec*TICKS_PER_SECOND);
			int32 stime	=	startkey;

			Quat l90;
			QuatT tmp; 
			l90.SetRotationZ(-IDENTITY);


			for (int32 t=0; t<info.realkeys; t++)
			{
				pLocomotionController->GetOPS(GAID, f32(stime-startkey)/(TICKS_PER_FRAME*(info.realkeys-1)),tmp.q,tmp.t,Scale);	
///				arrFullQTKeys0[t].t.z = 0.0f;
				arrFullTimes0[t]=stime;
				tmp.t -= firstpos;// - tmp.t;
				stime += TICKS_PER_FRAME;
				tmp.q = tmp.q * l90;
				//tmp.t = tmpfirst *tmp.t;
				tmp = tmpfirst * tmp;
				pqlog.vRotLog	=	log(!tmp.q);
				pqlog.vPos		=	tmp.t;
				_arrFullKeys0.push_back(pqlog);

			}
			CreateNewController(arrFullTimes0, _arrFullKeys0, Bip01, 0.01f * (float)m_AnimDesc.m_fRootQuality, 0.01f * (float)m_AnimDesc.m_fRootQuality);
			pController0 = m_GlobalAnimationHeader.GetController(Bip01ControllerID);

		}
		else
		{


			f32 duration = m_GlobalAnimationHeader.m_fEndSec - m_GlobalAnimationHeader.m_fStartSec;

			f32 PosesPerKey = (1.0f/60.0f);
			f32 HowManyPoses = uint32(duration/PosesPerKey);


			//----------------------------------------------------------
			//-------    reduced root-keys                          ----
			//----------------------------------------------------------
			PQLog pqlog;
			int qr_ideltical = arrFullQTKeys0[0].IsEquivalent(arrFullQTKeys0[ERot0.realkeys-1]);
			int q_ideltical  = arrFullQTKeys0[0].q.IsEquivalent(arrFullQTKeys0[ERot0.realkeys-1].q);
			if (qr_ideltical)
			{		
				//rotation and position is equal = we need one single keyframe
				pqlog.vRotLog	=	log(!arrFullQTKeys0[0].q);
				pqlog.vPos		=	arrFullQTKeys0[0].t;
				float rootPos = 0.0f;
				if (bRootToHeels)
				{
					//	int newtime = f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)) * 60.0f;//
					rootPos = (tmpSkeletons[0][lheel_idx].m_AbsoluteQuat.t.z + tmpSkeletons[0][rheel_idx].m_AbsoluteQuat.t.z)/2.0f;
				}


				pqlog.vPos.z	=	rootPos;
				_arrFullKeys0.push_back( pqlog );
				_arrFullTimes0.push_back( arrFullTimes0[0] );
				//pController0->SetControllerData( _arrFullKeys0, _arrFullTimes0 );
				CreateNewController(_arrFullTimes0, _arrFullKeys0, Bip01);
				pController0 = m_GlobalAnimationHeader.GetController(Bip01ControllerID);
			}
			else 
			{
				if (false)//q_ideltical)
				{
					stime = startkey;
					for (int32 t=0; t<ERot0.realkeys; t++)
					{
						float rootPos = 0.0f;
						if (bRootToHeels)
						{
							int newtime = f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys)) * HowManyPoses;//
							rootPos = (tmpSkeletons[newtime][lheel_idx].m_AbsoluteQuat.t.z + tmpSkeletons[newtime][rheel_idx].m_AbsoluteQuat.t.z)/2.0f;
						}
						stime += TICKS_PER_FRAME;

						arrFullQTKeys0[t].t.z =	rootPos;
						//	arrFullQTKeys0[t].t.x =	0.0f;
						arrFullQTKeys0[t].q.SetRotationZ(-gf_PI/2);
					}

					int32 idx;
					for (idx = 0; idx < ERot0.realkeys; ++idx)
					{

						pqlog.vRotLog	=	log(!arrFullQTKeys0[idx].q);
						pqlog.vPos		=	arrFullQTKeys0[idx].t;

						//ÏÎ×ÈÍÈ ÇÄÅÑÜ!!!!!!!!!
						//if (bRootToHeels)
						//{
						//	int newtime = (1.0f/60.0f)*idx; 
						//	rootPos = (tmpSkeletons[newtime][lheel_idx].m_AbsoluteQuat.t.z + tmpSkeletons[newtime][rheel_idx].m_AbsoluteQuat.t.z)/2.0f;
						//}
						//else
						//{
						//	rootPos = 0.0f;
						//}

						_arrFullKeys0.push_back( pqlog );
						_arrFullTimes0.push_back( arrFullTimes0[idx] );
					}
					CreateNewController(_arrFullTimes0, _arrFullKeys0, Bip01, 0.01f * (float)m_AnimDesc.m_fRootQuality, 0.01f * (float)m_AnimDesc.m_fRootQuality);
					pController0 = m_GlobalAnimationHeader.GetController(Bip01ControllerID);

					//int32 idx;

					//pqlog.vRotLog	=	log(!arrFullQTKeys0[0].q);
					//pqlog.vPos		=	arrFullQTKeys0[0].t;
					//pqlog.vPos.z	=	0;
					//uint32 key0 = arrFullTimes0[0];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key0 );

					//Vec3 dir = arrFullQTKeys0[ERot0.realkeys-1].t - arrFullQTKeys0[0].t;
					//for (idx = 0; idx < ERot0.realkeys; ++idx)
					//{

					//	Vec3 curDir = arrFullQTKeys0[idx].t - arrFullQTKeys0[0].t;
					//	if (fabs(curDir.Dot(Dir)) )
					//		continue;
					//	pqlog.vRotLog	=	log(!arrFullQTKeys0[idx].q);
					//	pqlog.vPos		=	arrFullQTKeys0[idx].t;
					//	pqlog.vPos.z	=	0;
					//	_arrFullKeys0.push_back( pqlog );
					//	_arrFullTimes0.push_back( arrFullTimes0[idx] );
					//}
					//CreateNewController(_arrFullTimes0, _arrFullKeys0, 0, 0.005f, 0.005f);
					//pController0 = m_GlobalAnimationHeader.GetController(m_pInfo->m_SkinningInfo.m_arrBonesDesc[Bip01].m_nControllerID);


					//uint32 num1= _arrFullKeys0.size();
					//uint32 num2= _arrFullTimes0.size();



					//pqlog.vRotLog	=	log(!arrFullQTKeys0[0].q);
					//pqlog.vPos		=	arrFullQTKeys0[0].t;
					//pqlog.vPos.z	=	0;
					//uint32 key0 = arrFullTimes0[0];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key0 );

					//pqlog.vRotLog	=	log(!arrFullQTKeys0[1].q);
					//pqlog.vPos		=	arrFullQTKeys0[1].t;
					//pqlog.vPos.z	=	0;
					//uint32 key1 = arrFullTimes0[1];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key1 );

					//pqlog.vRotLog	=	log(!arrFullQTKeys0[2].q);
					//pqlog.vPos		=	arrFullQTKeys0[1].t;
					//pqlog.vPos.z	=	0;
					//uint32 key2 = arrFullTimes0[1];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key2 );

					//pqlog.vRotLog	=	log(!arrFullQTKeys0[ERot0.realkeys-3].q);
					//pqlog.vPos		=	arrFullQTKeys0[ERot0.realkeys-3].t;
					//pqlog.vPos.z	=	0;
					//uint32 key3 = arrFullTimes0[ERot0.realkeys-3];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key3 );

					//pqlog.vRotLog	=	log(!arrFullQTKeys0[ERot0.realkeys-2].q);
					//pqlog.vPos		=	arrFullQTKeys0[ERot0.realkeys-2].t;
					//pqlog.vPos.z	=	0;
					//uint32 key4 = arrFullTimes0[ERot0.realkeys-2];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key4 );

					//pqlog.vRotLog	=	log(!arrFullQTKeys0[ERot0.realkeys-1].q);
					//pqlog.vPos		=	arrFullQTKeys0[ERot0.realkeys-1].t;
					//pqlog.vPos.z	=	0;
					//uint32 key5 = arrFullTimes0[ERot0.realkeys-1];
					//_arrFullKeys0.push_back( pqlog );
					//_arrFullTimes0.push_back( key5 );

					//CreateNewController(_arrFullTimes0, _arrFullKeys0, 0);
					//pController0 = m_GlobalAnimationHeader.GetController(m_pInfo->m_SkinningInfo.m_arrBonesDesc[Bip01].m_nControllerID);

				} 
				else
				{
					int32 idx;
					float step = 1.0f/ERot0.realkeys;


					stime = startkey;
					for (idx = 0; idx < ERot0.realkeys; ++idx)
					{

						pqlog.vRotLog	=	log(!arrFullQTKeys0[idx].q);
						pqlog.vPos		=	arrFullQTKeys0[idx].t;

						float rootPos=0.0f;
						if (bRootToHeels)
						{
							int newtime = f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys)) * HowManyPoses;///60.0f;//
							rootPos = (tmpSkeletons[newtime][lheel_idx].m_AbsoluteQuat.t.z + tmpSkeletons[newtime][rheel_idx].m_AbsoluteQuat.t.z)/2.0f;
						}
						stime += TICKS_PER_FRAME;

						pqlog.vPos.z	=	rootPos;
						_arrFullKeys0.push_back( pqlog );
						_arrFullTimes0.push_back( arrFullTimes0[idx] );
					}
					CreateNewController(_arrFullTimes0, _arrFullKeys0, Bip01, 0.01f * (float)m_AnimDesc.m_fRootQuality, 0.01f * (float)m_AnimDesc.m_fRootQuality);
					pController0 = m_GlobalAnimationHeader.GetController(Bip01ControllerID);

				}
			}

		}
		stime	=	startkey;
		arrFullTimes0.resize(ERot0.realkeys);
		arrFullQTKeys0.resize(ERot0.realkeys);
		for (int32 t=0; t<ERot0.realkeys; t++)
		{
			pController0->GetOPS(GAID, f32(stime-startkey)/(TICKS_PER_FRAME*(ERot0.realkeys-1)),arrFullQTKeys0[t].q,arrFullQTKeys0[t].t,Scale);	
			arrFullTimes0[t]=stime;
			stime += TICKS_PER_FRAME;
		}

		//----------------------------------------------------------------------------------------------------

		for (int32 t=0; t<ERot0.realkeys; t++)
		{
			Vec3 postmp = arrFullQTKeys1[t].t;
			arrFullQTKeys1[t] = arrFullQTKeys0[t].GetInverted() * arrFullQTKeys1[t]; //relative Pivot
			//QuatT inv = QuatT(_arrFullKeys0[t].);
			//inv.
			//arrFullQTKeys1[t] = inv.GetInvertedFast() * arrFullQTKeys1[t]; //relative Pivot
			arrFullKeys1[t].vRotLog	=	log(!arrFullQTKeys1[t].q);
			arrFullKeys1[t].vPos		=	arrFullQTKeys1[t].t ;
		}
		CreateNewController(arrFullTimes1, arrFullKeys1, Bip01Pelvis);
	}

}

void CAnimation::CreateSkeletonArray(  std::vector< std::vector<DebugJoint> >& arrSkeletons, GlobalAnimationHeader& header  ) 
{

	Diag33 temp;
	GlobalAnimationHeader& rGAH = header;

	f32 duration = rGAH.m_fEndSec - rGAH.m_fStartSec;

	f64 PosesPerKey = (1.0/60.0);
	uint32 HowManyPoses = uint32(duration/PosesPerKey);
	if (HowManyPoses==0)
		HowManyPoses=1;

//	rGAH.m_FootPlantBits.resize(HowManyPoses, 0);

	arrSkeletons.resize(HowManyPoses);
	uint32 numJoints = m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();

	for(uint32 i=0; i<HowManyPoses; i++)
		arrSkeletons[i].resize(numJoints);

	const CryBoneDescData* pModelJoint = &m_pInfo->m_SkinningInfo.m_arrBonesDesc[0];
	for (uint32 s=0; s<HowManyPoses; s++) 
	{
		//evaluate all controllers for this animation
		f32 newtime = (1.0f/HowManyPoses)*s; 
		for (uint32 j=0; j<numJoints; j++)
		{
			////m_DefaultRel; 
			if (pModelJoint[j].m_nOffsetParent != 0)
			{
				arrSkeletons[s][j].m_RelativeQuat= QuatT(pModelJoint[j+pModelJoint[j].m_nOffsetParent].m_DefaultW2B * pModelJoint[j].m_DefaultB2W);
			}
			else
			{
				arrSkeletons[s][j].m_RelativeQuat= QuatT(pModelJoint[j].m_DefaultB2W);
			}

			IController* pController =  rGAH.GetController(pModelJoint[j].m_nControllerID);
			if (pController)
				pController->GetOPS(GAID, newtime, arrSkeletons[s][j].m_RelativeQuat.q, arrSkeletons[s][j].m_RelativeQuat.t, temp );
		}

		//calculate absolute joints
		numJoints = m_pInfo->m_SkinningInfo.m_arrBonesDesc.size();
		arrSkeletons[s][0].m_AbsoluteQuat = arrSkeletons[s][0].m_RelativeQuat;
		for (uint32 i=1; i<numJoints; i++)
		{
			int16 idx = arrSkeletons[s][i].m_idxParent =	i + m_pInfo->m_SkinningInfo.m_arrBonesDesc[i].m_nOffsetParent;//m_pModel->m_arrModelJoints[i].m_idxParent;
			if (idx >= 0)
			{
				arrSkeletons[s][i].m_AbsoluteQuat	= arrSkeletons[s][idx].m_AbsoluteQuat * arrSkeletons[s][i].m_RelativeQuat;
				arrSkeletons[s][i].m_AbsoluteQuat.q.Normalize();
			}
		}

	}
}


float sqrt_error_quat(Quat& q1, Quat& q2)
{

	Float4Storage dist;

	dist.x= fabs(q1.v.x) - fabs(q2.v.x);
	dist.y= fabs(q1.v.y) - fabs(q2.v.y);
	dist.z= fabs(q1.v.z) - fabs(q2.v.z);
	dist.w= fabs(q1.w) - fabs(q2.w);

	float curerr = dist.x * dist.x  + dist.y * dist.y + dist.z * dist.z + dist.w * dist.w;
	return sqrtf(curerr);
}


bool CAnimation::GetError( std::vector< std::vector<DebugJoint> >& arrSkeletons1, std::vector< std::vector<DebugJoint> >& arrSkeletons2, int nJoint, float error ) 
{
	float maxerror = 0.0f;
	uint32 index = 0; 
	bool passed(true);

	for (uint32 i = 0, num = arrSkeletons1.size(); i < num; ++i)
	{

		float currerr = sqrt_error_quat(arrSkeletons1[i][nJoint].m_AbsoluteQuat.q, arrSkeletons2[i][nJoint].m_AbsoluteQuat.q);
		if (currerr > error)
		{
			maxerror = currerr;
			index = i;
			passed = false;
		}
	}

	return passed;
}


#define LHEEL (0x01)
#define RHEEL (0x02)
#define LTOE0 (0x04)
#define RTOE0 (0x08)
#define LNUB0 (0x10)
#define RNUB0 (0x20)

#define POSES (0x080)


void CAnimation::SetFootplantBitsAutomatically( std::vector< std::vector<DebugJoint> >& arrSkeletons, int32 nAnimID,int32 nGlobalAnimID,int32 lHidx,int32 rHidx,int32 lTidx,int32 rTidx,int32 lNidx,int32 rNidx, const SAnimationDesc& desc )
{

	CreateSkeletonArray( arrSkeletons, m_GlobalAnimationHeader);

//#define FLOORDIST (0.03f)
//#define SPEED_XY (0.005f)

	GlobalAnimationHeader& rGlobalAnimHeader = m_GlobalAnimationHeader;//CAnimationManager::GetInst().m_arrGlobalAnimations[nGlobalAnimID];

	std::vector<f32> g_arrLHeelVelocity;
	std::vector<f32> g_arrLToe0Velocity;
	std::vector<f32> g_arrLToeNub0Velocity;
	std::vector<f32> g_arrRHeelVelocity;
	std::vector<f32> g_arrRToe0Velocity;
	std::vector<f32> g_arrRToeNub0Velocity;


	uint32 numPoses=arrSkeletons.size();

	if (lHidx>0 && rHidx>0 && lTidx>0 && rTidx>0 && lNidx>0 && rNidx>0)
	{
		//arrFootPlants.resize(numPoses);
		//nGlobalAnimID
		rGlobalAnimHeader.m_FootPlantBits.resize(numPoses, 0);
		for (uint32 i=0; i<numPoses; i++)	
			rGlobalAnimHeader.m_FootPlantBits[i]=0;

		g_arrLHeelVelocity.resize(numPoses);
		g_arrRHeelVelocity.resize(numPoses);
		g_arrLToe0Velocity.resize(numPoses);
		g_arrRToe0Velocity.resize(numPoses);
		g_arrLToeNub0Velocity.resize(numPoses);
		g_arrRToeNub0Velocity.resize(numPoses);

		for (uint32 s=0; s<numPoses; s++) 
		{
			g_arrLHeelVelocity[s]=1000.0f;
			g_arrRHeelVelocity[s]=1000.0f;

			g_arrLToe0Velocity[s]=1000.0f;
			g_arrRToe0Velocity[s]=1000.0f;

			g_arrLToeNub0Velocity[s]=1000.0f;
			g_arrRToeNub0Velocity[s]=1000.0f;
		}


		Plane plane;
		f32 sloperad = rGlobalAnimHeader.m_fSlope;
		plane.n=Matrix33::CreateRotationX( sloperad ) * Vec3(0,0,1);
		plane.d=0;

		f32 dist=0;
		Vec3 sq; Vec3 zp;
		for (uint32 s=1; s<numPoses; s++) 
		{
			sq= arrSkeletons[s][lHidx].m_AbsoluteQuat.t - arrSkeletons[s-1][lHidx].m_AbsoluteQuat.t; sq.z=0;
			zp=(arrSkeletons[s][lHidx].m_AbsoluteQuat.t + arrSkeletons[s-1][lHidx].m_AbsoluteQuat.t )*0.5f;
			dist=/*fabsf*/(plane|zp);
			g_arrLHeelVelocity[s]=(dist < desc.m_fFloorDist) ? sq.GetLength() : 1000.0f;

			sq= arrSkeletons[s][rHidx].m_AbsoluteQuat.t - arrSkeletons[s-1][rHidx].m_AbsoluteQuat.t; sq.z=0;
			zp=(arrSkeletons[s][rHidx].m_AbsoluteQuat.t + arrSkeletons[s-1][rHidx].m_AbsoluteQuat.t )*0.5f;
			dist=/*fabsf*/(plane|zp);
			g_arrRHeelVelocity[s]=(dist < desc.m_fFloorDist) ? sq.GetLength() : 1000.0f;;

			sq= arrSkeletons[s][lTidx].m_AbsoluteQuat.t - arrSkeletons[s-1][lTidx].m_AbsoluteQuat.t; sq.z=0;
			zp=(arrSkeletons[s][lTidx].m_AbsoluteQuat.t + arrSkeletons[s-1][lTidx].m_AbsoluteQuat.t)*0.5f;
			dist=/*fabsf*/(plane|zp);
			g_arrLToe0Velocity[s]=(dist < desc.m_fFloorDist) ? sq.GetLength() : 1000.0f;
			sq= arrSkeletons[s][rTidx].m_AbsoluteQuat.t - arrSkeletons[s-1][rTidx].m_AbsoluteQuat.t; sq.z=0;
			zp=(arrSkeletons[s][rTidx].m_AbsoluteQuat.t + arrSkeletons[s-1][rTidx].m_AbsoluteQuat.t )*0.5f;
			dist=/*fabsf*/(plane|zp);
			g_arrRToe0Velocity[s]=(dist < desc.m_fFloorDist) ? sq.GetLength() : 1000.0f;

			sq= arrSkeletons[s][lNidx].m_AbsoluteQuat.t - arrSkeletons[s-1][lNidx].m_AbsoluteQuat.t; sq.z=0;
			zp=(arrSkeletons[s][lNidx].m_AbsoluteQuat.t + arrSkeletons[s-1][lNidx].m_AbsoluteQuat.t)*0.5f;
			dist=/*fabsf*/(plane|zp);
			g_arrLToeNub0Velocity[s]=(dist < desc.m_fFloorDist) ? sq.GetLength() : 1000.0f;
			sq= arrSkeletons[s][rNidx].m_AbsoluteQuat.t - arrSkeletons[s-1][rNidx].m_AbsoluteQuat.t; sq.z=0;
			zp=(arrSkeletons[s][rNidx].m_AbsoluteQuat.t + arrSkeletons[s-1][rNidx].m_AbsoluteQuat.t)*0.5f;
			dist=/*fabsf*/(plane|zp);
			g_arrRToeNub0Velocity[s]=(dist < desc.m_fFloorDist) ? sq.GetLength() : 1000.0f;
		}

		g_arrLHeelVelocity[0]=g_arrLHeelVelocity[numPoses-1];
		g_arrRHeelVelocity[0]=g_arrRHeelVelocity[numPoses-1];
		g_arrLToe0Velocity[0]=g_arrLToe0Velocity[numPoses-1];
		g_arrRToe0Velocity[0]=g_arrRToe0Velocity[numPoses-1];
		g_arrLToeNub0Velocity[0]=g_arrLToeNub0Velocity[numPoses-1];
		g_arrRToeNub0Velocity[0]=g_arrRToeNub0Velocity[numPoses-1];

		for (uint32 s=0; s<numPoses; s++) 
		{
			if (g_arrLHeelVelocity[s]<desc.m_fSpeedXY)
				rGlobalAnimHeader.m_FootPlantBits[s]|=LHEEL;
			if (g_arrRHeelVelocity[s]<desc.m_fSpeedXY)
				rGlobalAnimHeader.m_FootPlantBits[s]|=RHEEL;

			if (g_arrLToe0Velocity[s]<desc.m_fSpeedXY)
				rGlobalAnimHeader.m_FootPlantBits[s]|=LTOE0;
			if (g_arrRToe0Velocity[s]<desc.m_fSpeedXY)
				rGlobalAnimHeader.m_FootPlantBits[s]|=RTOE0;

			if (g_arrLToeNub0Velocity[s]<desc.m_fSpeedXY)
				rGlobalAnimHeader.m_FootPlantBits[s]|=LNUB0;
			if (g_arrRToeNub0Velocity[s]<desc.m_fSpeedXY)
				rGlobalAnimHeader.m_FootPlantBits[s]|=RNUB0;
		}

	}
}




void CAnimation::SetFootplantVectors( std::vector< std::vector<DebugJoint> >& arrSkeletons, uint32 nAnimID, SFootPlant& rFootPlants, uint32 nGlobalAnimID )
{

#define lm0 (10)
#define lm1 (11)
#define rm0 (13)
#define rm1 (14)

#define lx0 (16)
#define lx1 (17)
#define rx0 (18)
#define rx1 (19)


	GlobalAnimationHeader& rGlobalAnimHeader = m_GlobalAnimationHeader;//CAnimationManager::GetInst().m_arrGlobalAnimations[nGlobalAnimID];
	int32 poses0 = rGlobalAnimHeader.m_FootPlantBits.size();

	g_arrDistMap24.resize(poses0*20);
	for (int32 i=0; i<(poses0*20); i++)
	{
		g_arrDistMap24[i].r=0;
		g_arrDistMap24[i].g=0;
		g_arrDistMap24[i].b=0;
	}

	int32 hposes=poses0/2;
	for (int32 i=0; i<poses0; i++)
	{

		uint8 FootStep = rGlobalAnimHeader.m_FootPlantBits[i];
		if (FootStep&LHEEL)
			g_arrDistMap24[i+poses0*0].b=0xff;
		if (FootStep&LTOE0)
			g_arrDistMap24[i+poses0*1].r=0xff;
		if (FootStep&LNUB0)
			g_arrDistMap24[i+poses0*2].g=0xff;

		if (FootStep&RHEEL)
			g_arrDistMap24[i+poses0*5].b=0xff;
		if (FootStep&RTOE0)
			g_arrDistMap24[i+poses0*6].r=0xff;
		if (FootStep&RNUB0)
			g_arrDistMap24[i+poses0*7].g=0xff;

		//---------------------------------------------------
		if (i<hposes)
		{
			assert((i+hposes)<poses0);
			if (FootStep&LHEEL)
				g_arrDistMap24[i+poses0*lm0+hposes].b=0xff;
			if (FootStep&LTOE0)
				g_arrDistMap24[i+poses0*lm1+hposes].r=0xff;
			if (FootStep&LNUB0)
				g_arrDistMap24[i+poses0*lm1+hposes].r=0xff;
		} 
		else 
		{
			if (FootStep&LHEEL)
				g_arrDistMap24[i+poses0*lm0-hposes].b=0xff;
			if (FootStep&LTOE0)
				g_arrDistMap24[i+poses0*lm1-hposes].r=0xff;
			if (FootStep&LNUB0)
				g_arrDistMap24[i+poses0*lm1-hposes].r=0xff;
		}

		if (FootStep&RHEEL)
			g_arrDistMap24[i+poses0*rm0].b=0xff;
		if (FootStep&RTOE0)
			g_arrDistMap24[i+poses0*rm1].r=0xff;
		if (FootStep&RNUB0)
			g_arrDistMap24[i+poses0*rm1].r=0xff;
	}		

	f32 r=0.05f;
	//delete area in footsteps
	for (f32 i=0.0f; i<r; i=i+0.001f)
	{
		//start of step
		uint32 idx0=(uint32)(i*(poses0));
		g_arrDistMap24[idx0+poses0*lm0].b=0;
		g_arrDistMap24[idx0+poses0*lm1].r=0;
		g_arrDistMap24[idx0+poses0*rm0].b=0;
		g_arrDistMap24[idx0+poses0*rm1].r=0;

		//end of step
		uint32 idx1=(uint32)( (1.0f-r+i) *(poses0));
		assert(idx1<(uint32)poses0);
		g_arrDistMap24[idx1+poses0*lm0].b=0;
		g_arrDistMap24[idx1+poses0*lm1].r=0;
		g_arrDistMap24[idx1+poses0*rm0].b=0;
		g_arrDistMap24[idx1+poses0*rm1].r=0;
	}

	int16 lhs=-1;
	int16 lhe=-1;
	int16 lts=-1;
	int16 lte=-1;
	int16 rhs=-1;
	int16 rhe=-1;
	int16 rts=-1;
	int16 rte=-1;
	for (int32 i=0,t=poses0-1; i<poses0; i++,t--)
	{
		if (lhs==-1)
			if (g_arrDistMap24[i+poses0*lm0].b==0xff) lhs=i;
		if (lts==-1)
			if (g_arrDistMap24[i+poses0*lm1].r==0xff) lts=i;
		if (lhe==-1)
			if (g_arrDistMap24[t+poses0*lm0].b==0xff) lhe=t;
		if (lte==-1)
			if (g_arrDistMap24[t+poses0*lm1].r==0xff) lte=t;

		if (rhs==-1)
			if (g_arrDistMap24[i+poses0*rm0].b==0xff) rhs=i;
		if (rts==-1)
			if (g_arrDistMap24[i+poses0*rm1].r==0xff) rts=i;
		if (rhe==-1)
			if (g_arrDistMap24[t+poses0*rm0].b==0xff) rhe=t;
		if (rte==-1)
			if (g_arrDistMap24[t+poses0*rm1].r==0xff) rte=t;
	}

	if (lhs!=-1 && lhe!=-1)
		for (int32 i=lhs; i<lhe; i++)	g_arrDistMap24[i+poses0*lm0].b=0xff;
	if (lts!=-1 && lte!=-1)
		for (int32 i=lts; i<lte; i++)	g_arrDistMap24[i+poses0*lm1].r=0xff;

	if (rhs!=-1 && rhe!=-1)
		for (int32 i=rhs; i<rhe; i++)	g_arrDistMap24[i+poses0*rm0].b=0xff;
	if (rts!=-1 && rte!=-1)
		for (int32 i=rts; i<rte; i++)	g_arrDistMap24[i+poses0*rm1].r=0xff;

	//------------------------------------------------------------------------------------

	lhs=-1;
	lhe=-1;
	lts=-1;
	lte=-1;
	rhs=-1;
	rhe=-1;
	rts=-1;
	rte=-1;
	for (int32 i=0,t=poses0-1; i<poses0; i++,t--)
	{
		if (lhs==-1)
			if (g_arrDistMap24[i+poses0*lm0].b==0xff) lhs=i;
		if (lts==-1)
			if (g_arrDistMap24[i+poses0*lm1].r==0xff) lts=i;
		if (lhe==-1)
			if (g_arrDistMap24[t+poses0*lm0].b==0xff) lhe=t;
		if (lte==-1)
			if (g_arrDistMap24[t+poses0*lm1].r==0xff) lte=t;

		if (rhs==-1)
			if (g_arrDistMap24[i+poses0*rm0].b==0xff) rhs=i;
		if (rts==-1)
			if (g_arrDistMap24[i+poses0*rm1].r==0xff) rts=i;
		if (rhe==-1)
			if (g_arrDistMap24[t+poses0*rm0].b==0xff) rhe=t;
		if (rte==-1)
			if (g_arrDistMap24[t+poses0*rm1].r==0xff) rte=t;
	}

	if (lhs!=-1 && lhe!=-1 && rhs!=-1 && rhe!=-1)
	{
		if ( (lhe-hposes)>rhs )
		{
			int32 slh=((lhe-hposes)+rhs)/2;
			g_arrDistMap24[slh+hposes+poses0*lm0].g=0xff;	//left heel start
			g_arrDistMap24[slh+poses0*rm0].g=0x3f;	//right heel end
			for (int32 i=(slh+hposes); i<poses0; i++)
				g_arrDistMap24[i+poses0*lm0].b=0;	//left heel start
			for (int32 i=0; i<slh; i++)
				g_arrDistMap24[i+poses0*rm0].b=0;	//right heel start
		}

		if ( (lhs+hposes)<rhe )
		{
			int32 elh=((lhs+hposes)+rhe)/2;
			g_arrDistMap24[elh-hposes+poses0*lm0].g=0x7f;	//left heel start
			g_arrDistMap24[elh+poses0*rm0].g=0x3f;	//right heel end
			for (int32 i=0; i<(elh-hposes); i++)
				g_arrDistMap24[i+poses0*lm0].b=0;	//left heel start
			for (int32 i=elh; i<poses0; i++)
				g_arrDistMap24[i+poses0*rm0].b=0;	//right heel end
		}
	}

	if (lts!=-1 && lte!=-1 && rts!=-1 && rte!=-1)
	{
		if ( (lte-hposes)>rts )
		{
			int32 slt=((lte-hposes)+rts)/2;
			//g_arrDistMap24[slt+hposes+poses0*lm1].g=0xff;	//right toe start
			//g_arrDistMap24[slt+poses0*rm1].g=0x3f;	//left toe start
			for (int32 i=(slt+hposes); i<poses0; i++)
				g_arrDistMap24[i+poses0*lm1].r=0;	//right toe start
			for (int32 i=0; i<slt; i++)
				g_arrDistMap24[i+poses0*rm1].r=0;	//left toe start
		}

		if ( (lts+hposes)<rte )
		{
			int32 elt=((lts+hposes)+rte)/2;
			//g_arrDistMap24[elt-hposes+poses0*lm1].g=0x7f;	//left toe start
			//g_arrDistMap24[elt+poses0*rm1].g=0x3f;	//right toe end
			for (int32 i=0; i<(elt-hposes); i++)
				g_arrDistMap24[i+poses0*lm1].r=0;	//left toe start
			for (int32 i=elt; i<poses0; i++)
				g_arrDistMap24[i+poses0*rm1].r=0;	//right toe end
		}
	}

	//-----------------------------------------------------------------------------

	for (int32 i=0; i<poses0; i++)
	{
		if (i<hposes)
		{
			g_arrDistMap24[i+poses0*lx0+hposes].b=g_arrDistMap24[i+poses0*lm0].b;	//left toe start
			g_arrDistMap24[i+poses0*lx1+hposes].r=g_arrDistMap24[i+poses0*lm1].r;	//left toe start
		}
		else
		{
			g_arrDistMap24[i+poses0*lx0-hposes].b=g_arrDistMap24[i+poses0*lm0].b;	//left toe start
			g_arrDistMap24[i+poses0*lx1-hposes].r=g_arrDistMap24[i+poses0*lm1].r;	//left toe start
		}

		g_arrDistMap24[i+poses0*rx0].b=g_arrDistMap24[i+poses0*rm0].b;	//left toe start
		g_arrDistMap24[i+poses0*rx1].r=g_arrDistMap24[i+poses0*rm1].r;	//left toe start
	}

	for (int32 i=0; i<poses0; i++)
	{
		uint32 footplant=0;
		if (g_arrDistMap24[i+poses0*lx0].b==0xff)
			footplant|=LHEEL;
		if (g_arrDistMap24[i+poses0*lx1].r==0xff)
			footplant|=LTOE0;
		if (g_arrDistMap24[i+poses0*rx0].b==0xff)
			footplant|=RHEEL;
		if (g_arrDistMap24[i+poses0*rx1].r==0xff)
			footplant|=RTOE0;

		rGlobalAnimHeader.m_FootPlantBits[i]=footplant;
	}

	//------------------------------------------------------------------

	lhs=-1;
	lhe=-1;
	lts=-1;
	lte=-1;
	rhs=-1;
	rhe=-1;
	rts=-1;
	rte=-1;
	for (int32 i=0,t=poses0-1; i<poses0; i++,t--)
	{
		if (lhs==-1)
			if (g_arrDistMap24[i+poses0*lm0].b==0xff) lhs=i;
		if (lts==-1)
			if (g_arrDistMap24[i+poses0*lm1].r==0xff) lts=i;
		if (lhe==-1)
			if (g_arrDistMap24[t+poses0*lm0].b==0xff) lhe=t;
		if (lte==-1)
			if (g_arrDistMap24[t+poses0*lm1].r==0xff) lte=t;

		if (rhs==-1)
			if (g_arrDistMap24[i+poses0*rm0].b==0xff) rhs=i;
		if (rts==-1)
			if (g_arrDistMap24[i+poses0*rm1].r==0xff) rts=i;
		if (rhe==-1)
			if (g_arrDistMap24[t+poses0*rm0].b==0xff) rhe=t;
		if (rte==-1)
			if (g_arrDistMap24[t+poses0*rm1].r==0xff) rte=t;
	}


	f32 pose = f32(poses0);

	if (lhs!=-1)
		rFootPlants.m_LHeelStart	=	lhs/pose;
	if (lhe!=-1)
		rFootPlants.m_LHeelEnd		=	lhe/pose;
	if (lts!=-1)
		rFootPlants.m_LToe0Start	=	lts/pose;
	if (lte!=-1)
		rFootPlants.m_LToe0End		=	lte/pose;

	if (rhs!=-1)
		rFootPlants.m_RHeelStart	=	rhs/pose;;
	if (rhe!=-1)
		rFootPlants.m_RHeelEnd		=	rhe/pose;;
	if (rts!=-1)
		rFootPlants.m_RToe0Start	=	rts/pose;;
	if (rte!=-1)
		rFootPlants.m_RToe0End		=	rte/pose;;

}

bool IsOldChunk(CChunkFile::ChunkDesc * ch)
{
	return (ch->hdr.ChunkVersion < CONTROLLER_CHUNK_DESC_0829::VERSION ) || (ch->hdr.ChunkVersion ==  CONTROLLER_CHUNK_DESC_0831::VERSION);
}

bool IsNewChunk(CChunkFile::ChunkDesc * ch)
{
	return !IsOldChunk(ch);
}

void CAnimation::DeleteOldChunk(EDeleteMethod delFlag, bool bEraseAnotherChunks)
{

	// cleanup all chunks
	if (bEraseAnotherChunks)
	{
		while (true) {
			CChunkFile::ChunkDesc *cd = m_ChunkFile.FindChunkByType(ChunkType_SpeedInfo);
			if (cd)
				m_ChunkFile.DeleteChunkId( cd->hdr.ChunkID );
			else
				break;
		}

		while (true) {
			CChunkFile::ChunkDesc *cd = m_ChunkFile.FindChunkByType(ChunkType_FootPlantInfo);
			if (cd)
				m_ChunkFile.DeleteChunkId( cd->hdr.ChunkID );
			else
				break;
		}

		while (true) {
			CChunkFile::ChunkDesc *cd = m_ChunkFile.FindChunkByType(ChunkType_BoneNameList);
			if (cd)
				m_ChunkFile.DeleteChunkId( cd->hdr.ChunkID );
			else
				break;
		}
	}

	// delete controller info
	if (delFlag != eSkipDelete)
	{
		uint32 numChunck = m_ChunkFile.NumChunks();
		for (uint32 i=0; i<numChunck; i++)
		{

			CChunkFile::ChunkDesc *cd = m_ChunkFile.GetChunk(i);
			if (cd)
			{
				if (cd->hdr.ChunkType != ChunkType_Controller)
					continue;

				if (delFlag == eAll || ((delFlag == eOld) && IsOldChunk(cd) ) || ((delFlag == eNew) && IsNewChunk(cd)))
				{
					m_ChunkFile.DeleteChunkId( cd->hdr.ChunkID );
					--numChunck;
					i = 0;
				}
			}
		}
	}


}


bool CAnimation::CompareKeyTimes(KeyTimesInformationPtr& ptr1, KeyTimesInformationPtr& ptr2)
{

	if (ptr1->GetNumKeys() != ptr2->GetNumKeys())
	{
		return false;
	}

	for (uint32 i = 0; i < ptr1->GetNumKeys(); ++i)
	{
		if (ptr1->GetKeyValueFloat(i) != ptr2->GetKeyValueFloat(i))
		{
			return false;
		}
	}
	return true;
}


struct CChunkData
{
	char *data;
	int size;

	//////////////////////////////////////////////////////////////////////////
	CChunkData() { data = 0; size = 0; }
	~CChunkData() { free(data); }

	template <class T>
	void Add( const T& object )
	{
		AddData( &object,sizeof(object) );
	}
	void AddData( const void *pSrcData,int nSrcDataSize )
	{
		data = (char*)realloc(data,size+nSrcDataSize);
		memcpy( data+size,pSrcData,nSrcDataSize );
		size += nSrcDataSize;
	}
};



int  CAnimation::SaveControllers(CSaverCGF& saver, TControllersVector& m_arrController)
{

	for (uint32 numChunks = 0, end = m_arrController.size();  numChunks < end; ++numChunks)
	{
		{
			CControllerPQLog *pController  = dynamic_cast<CControllerPQLog *>(m_arrController[numChunks].get());

			if (pController)
			{
				std::auto_ptr<CryKeyPQLog>  pKeys;

				int numKeys = m_arrController[numChunks]->GetControllerInfo().numKeys;//pInfo->m_arrTracksTimes[numChunks].size();
				int controllerID = m_arrController[numChunks]->GetID();//pInfo->m_arrControllerId[numChunks];

				pKeys.reset(new CryKeyPQLog[numKeys]);

				CControllerPQLog * pController = (CControllerPQLog *)m_arrController[numChunks].get();
				for (uint32 i=0; i<numKeys; ++i)
				{
					pKeys.get()[i].nTime =  pController->m_arrTimes[i]  ;//pInfo->m_arrTracksTimes[numChunks].operator [](i); //TrackTimes[i];
					pKeys.get()[i].vPos = pController->m_arrKeys[i].vPos * 100.0f;;//pInfo->m_arrTracksPQLog[numChunks].operator [](i).vPos  * 100.0f;
					pKeys.get()[i].vRotLog = pController->m_arrKeys[i].vRotLog;//pInfo->m_arrTracksPQLog[numChunks].operator [](i).vRotLog;
				}

				saver.SaveController( numKeys, controllerID, pKeys.get(), numKeys );

				continue;
			}
		}

		{
			// new format controller
			CCompressedController * pController = dynamic_cast<CCompressedController *>(m_arrController[numChunks].get());
			if (pController)
			{
				SaveWaveletController(pController, saver);
				continue;
			}
		}

		{
			// new format controller
			CController * pController = dynamic_cast<CController *>(m_arrController[numChunks].get());
			if (pController)
			{
				SaveCController(pController, saver);
				continue;
			}
		}
	}

	return 1;
}

void CAnimation::SaveCController(CController * pController, CSaverCGF& saver)
{
	CONTROLLER_CHUNK_DESC_0829 chunk;

	uint32 nSize = 0;

	bool bPosUseRot(false);
	bool bScaleUseRot(false);
	bool bScaleUsePos(false);

	ZeroStruct(chunk);

	chunk.chdr.ChunkType = ChunkType_Controller;
	chunk.chdr.ChunkVersion = CONTROLLER_CHUNK_DESC_0829::VERSION;

	int s = sizeof(chunk);
	CChunkData Data;

	chunk.nControllerId = pController->GetID();
	SWAP SwapEndians(chunk.nControllerId);

	TrackInformationPtr pRotation = pController->GetRotationController();
	KeyTimesInformationPtr pRotTimes;
	if (pRotation)
	{
		pRotTimes = pRotation->GetKeyTimesInformation();
		chunk.numRotationKeys = pRotation->GetNumKeys();
		SWAP SwapEndians(chunk.numRotationKeys);

		chunk.RotationFormat = pRotation->GetFormat();
		SWAP SwapEndians(chunk.RotationFormat);


		chunk.RotationTimeFormat = pRotTimes->GetFormat();
		SWAP SwapEndians(chunk.RotationTimeFormat);

		//copy to chunk
		SWAP pRotation->GetRotationStorage()->SwapBytes();
		Data.AddData(pRotation->GetRotationStorage()->GetData(), pRotation->GetRotationStorage()->GetDataRawSize());
		SWAP pRotation->GetRotationStorage()->SwapBytes();

		SWAP pRotTimes->SwapBytes();
		Data.AddData(pRotTimes->GetData(), pRotTimes->GetDataRawSize());
		SWAP pRotTimes->SwapBytes();

		//// FOR TESTS!!!
		//FILE * f = fopen("e:\\compquat.txt", "wb");
		//Vec3 pos;
		//
		//for (uint32 a = 0; a < pRotation->GetNumKeys(); ++a)
		//{
		//	f32 time = pRotation->GetKeyTimesInformation()->GetKeyValueFloat(a);
		//	
		//	Quat quats;
		//	pRotation->GetValueFromKey(a, quats);
		//	Ang3 ang;
		//	ang = Ang3::GetAnglesXYZ(quats);
		//	fprintf(f, "%f %f %f %f %f %f %f\n", time, pos.x, pos.y, pos.z, ang.x, ang.y, ang.z);
		//}
		//fclose(f);
		//// END
	}

	PositionInformationPtr pPosition = pController->GetPositionController();
	KeyTimesInformationPtr pPosTimes;
	if (pPosition)
	{
		pPosTimes = pPosition->GetKeyTimesInformation();
		chunk.numPositionKeys = pPosition->GetNumKeys();
		SWAP SwapEndians(chunk.numPositionKeys);

		chunk.PositionFormat = pPosition->GetFormat();
		SWAP SwapEndians(chunk.PositionFormat);

		chunk.PositionKeysInfo = CONTROLLER_CHUNK_DESC_0829::eKeyTimePosition;

		SWAP pPosition->GetPositionStorage()->SwapBytes();
		Data.AddData(pPosition->GetPositionStorage()->GetData(), pPosition->GetPositionStorage()->GetDataRawSize());
		SWAP pPosition->GetPositionStorage()->SwapBytes();

		//// FOR TESTS!!!
		//FILE * f = fopen("e:\\compressed.txt", "wb");
		//Vec4 quat;
		//
		//for (uint32 a = 0; a < pPosition->GetNumKeys(); ++a)
		//{
		//	f32 time = pPosition->GetKeyTimesInformation()->GetKeyValueFloat(a);
		//	
		//	Vec3 pos;
		//	pPosition->GetValueFromKey(a, pos);
		//	fprintf(f, "%f %f %f %f %f %f %f\n", time, quat.x, quat.y, quat.z, pos.x, pos.y, pos.z);
		//}
		//fclose(f);
		//// END

		if (pRotation && CompareKeyTimes(pPosTimes, pRotTimes))
		{
			chunk.PositionKeysInfo = CONTROLLER_CHUNK_DESC_0829::eKeyTimeRotation;
		}
		else
		{
			SWAP pPosTimes->SwapBytes();

			Data.AddData(pPosTimes->GetData(), pPosTimes->GetDataRawSize());
			SWAP pPosTimes->SwapBytes();
			chunk.PositionTimeFormat = pPosTimes->GetFormat();
		}
	}

	saver.SaveController829(chunk, Data.data, Data.size );
}



void CAnimation::SaveWaveletController(CCompressedController * pController, CSaverCGF& saver)
{
	CONTROLLER_CHUNK_DESC_0830 chunk;
	CONTROLLER_CHUNK_DESC_0829 chunk1;

	uint32 nSize = 0;

	bool bPosUseRot(false);
	bool bScaleUseRot(false);
	bool bScaleUsePos(false);

	ZeroStruct(chunk);

	int s = sizeof(chunk);
	int s1 = sizeof(chunk1);

	chunk.chdr.ChunkType = ChunkType_Controller;
	chunk.chdr.ChunkVersion = CONTROLLER_CHUNK_DESC_0830::VERSION;

	CChunkData Data;

	chunk.nControllerId = pController->GetID();

	TrackInformationPtr pRotation = pController->GetRotationController();
	KeyTimesInformationPtr pRotTimes;// = pRotation->GetKeyTimesInformation();

	bool bWaveletRot(false);
	bool bWaveletPos(false);

	chunk.ChunkType = 0;

	if (pController->m_Rotations0.size() > 0)
	{
		// we have a wavelet compression
		bWaveletRot = true;
		pRotTimes = pRotation->GetKeyTimesInformation();
		chunk.RotationTimeFormat = pRotTimes->GetFormat();
		chunk.RotationFormat = pController->m_iRotationFormat;

		chunk.numRotationKeys = pRotTimes->GetNumKeys();

		Data.AddData(&(pController->m_Rotations0[0]), pController->m_Rotations0.size() * sizeof(int));
		Data.AddData(&(pController->m_Rotations1[0]), pController->m_Rotations1.size() * sizeof(int));
		Data.AddData(&(pController->m_Rotations2[0]), pController->m_Rotations2.size() * sizeof(int));
		Data.AddData(pRotTimes->GetData(), pRotTimes->GetDataRawSize());

		chunk.ChunkType = 1;
	}
	else
		if (pRotation)
		{
			pRotTimes = pRotation->GetKeyTimesInformation();
			chunk.numRotationKeys = pRotation->GetNumKeys();
			chunk.RotationFormat = pRotation->GetFormat();
			chunk.RotationTimeFormat = pRotTimes->GetFormat();

			//copy to chunk
			Data.AddData(pRotation->GetRotationStorage()->GetData(), pRotation->GetRotationStorage()->GetDataRawSize());
			Data.AddData(pRotTimes->GetData(), pRotTimes->GetDataRawSize());

			//// FOR TESTS!!!
			//FILE * f = fopen("e:\\compquat.txt", "wb");
			//Vec3 pos;
			//
			//for (uint32 a = 0; a < pRotation->GetNumKeys(); ++a)
			//{
			//	f32 time = pRotation->GetKeyTimesInformation()->GetKeyValueFloat(a);
			//	
			//	Quat quats;
			//	pRotation->GetValueFromKey(a, quats);
			//	Ang3 ang;
			//	ang = Ang3::GetAnglesXYZ(quats);
			//	fprintf(f, "%f %f %f %f %f %f %f\n", time, pos.x, pos.y, pos.z, ang.x, ang.y, ang.z);
			//}
			//fclose(f);
			//// END

		}

		PositionInformationPtr pPosition = pController->GetPositionController();
		KeyTimesInformationPtr pPosTimes;


		if (pController->m_Positions0.size() > 0)
		{
			pPosTimes = pPosition->GetKeyTimesInformation();

			chunk.PositionFormat = pController->m_iPositionFormat;//pPosition->GetFormat();
			chunk.PositionKeysInfo = CONTROLLER_CHUNK_DESC_0829::eKeyTimePosition;
			chunk.numPositionKeys = pPosition->GetKeyTimesInformation()->GetNumKeys();

			Data.AddData(&(pController->m_Positions0[0]), pController->m_Positions0.size() * sizeof(int));
			Data.AddData(&(pController->m_Positions1[0]), pController->m_Positions1.size() * sizeof(int));
			Data.AddData(&(pController->m_Positions2[0]), pController->m_Positions2.size() * sizeof(int));

			if (pRotation && CompareKeyTimes(pPosTimes, pRotTimes))
			{
				chunk.PositionKeysInfo = CONTROLLER_CHUNK_DESC_0829::eKeyTimeRotation;
			}
			else
			{
				Data.AddData(pPosTimes->GetData(), pPosTimes->GetDataRawSize());
				chunk.PositionTimeFormat = pPosTimes->GetFormat();
			}

			chunk.ChunkType |= 2;
		}
		else
			if (pPosition)
			{
				pPosTimes = pPosition->GetKeyTimesInformation();
				chunk.numPositionKeys = pPosition->GetKeyTimesInformation()->GetNumKeys();
				chunk.PositionFormat = pPosition->GetFormat();
				chunk.PositionKeysInfo = CONTROLLER_CHUNK_DESC_0829::eKeyTimePosition;


				Data.AddData(pPosition->GetPositionStorage()->GetData(), pPosition->GetPositionStorage()->GetDataRawSize());

				// FOR TESTS!
				//FILE * f = fopen("e:\\compressed.txt", "wb");
				//Vec4 quat;
				//
				//for (uint32 a = 0; a < pPosition->GetNumKeys(); ++a)
				//{
				//	f32 time = pPosition->GetKeyTimesInformation()->GetKeyValueFloat(a);
				//	
				//	Vec3 pos;
				//	pPosition->GetValueFromKey(a, pos);
				//	fprintf(f, "%f %f %f %f %f %f %f\n", time, quat.x, quat.y, quat.z, pos.x, pos.y, pos.z);
				//}
				//fclose(f);
				//// END

				if (pRotation && CompareKeyTimes(pPosTimes, pRotTimes))
				{
					chunk.PositionKeysInfo = CONTROLLER_CHUNK_DESC_0829::eKeyTimeRotation;
				}
				else
				{
					Data.AddData(pPosTimes->GetData(), pPosTimes->GetDataRawSize());
					chunk.PositionTimeFormat = pPosTimes->GetFormat();
				}
			}

			saver.SaveController830(chunk, Data.data, Data.size );
}


IController* GlobalAnimationHeader::GetController(uint32 nControllerID)
{
	uint32 numController = m_arrController.size();
	for (uint32 i=0; i<numController; i++) 
	{
		if ( m_arrController[i] && m_arrController[i]->GetID()==nControllerID )
			return m_arrController[i];
	}
	return NULL;
}

