
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ZAnim Class
///
///		- Animation Class (position et rotation)
///		- KeyFrame Animation!!
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



#include	"..\Scene Graph\ZooAnim.h"


 

///////////////////////////////////////////////////////////////////////////
/// Constructor					 										///
///////////////////////////////////////////////////////////////////////////
ZAnim::ZAnim(int s3d) : ZNodeGraph(s3d)
{
	type = ANI_TYPE_ID;

	listPos.resize(0);
	listRot.resize(0);

	maxKeyPos  = maxKeyRot = 0;
	minKeyPos  = minKeyRot = 100000000;

	length = 0;

}

ZAnim::~ZAnim()
{
	ZNodeGraph::~ZNodeGraph();
}



///////////////////////////////////////////////////////////////////////////
/// Add key position in the list 										///
///////////////////////////////////////////////////////////////////////////
bool ZAnim::addKeyPos(int key, const ZVector3 &pos)
{
	for(int i=0; i<listPos.size() && key>listPos[i].key; i++);

	if(i==listPos.size())
	{
		listPos.resize(listPos.size()+1);
		listPos[listPos.size()-1].key = key;
		listPos[listPos.size()-1].pos = pos;
	}
	else
	if(listPos[i].key == key)
	{
		return false;
	}
	else
	{
		listPos.resize(listPos.size()+1);
		
		for(int j=listPos.size()-1; j>i; j--)
		{
			listPos[j].key = listPos[j-1].key;
			listPos[j].pos = listPos[j-1].pos;
		}
		
		listPos[i].key = key;
		listPos[i].pos = pos;
	}

	if(key > maxKeyPos)		maxKeyPos = key;
	if(key < minKeyPos)		minKeyPos = key;
	
	return true;
}



///////////////////////////////////////////////////////////////////////////
/// Add key rotation in the list 										///
///////////////////////////////////////////////////////////////////////////
bool ZAnim::addKeyRot(int key, const ZQuat &rot)
{
	for(int i=0; i<listRot.size() && key>listRot[i].key; i++);

	if(i==listRot.size())
	{
		listRot.resize(listRot.size()+1);
		listRot[listRot.size()-1].key = key;
		listRot[listRot.size()-1].rot = rot;
	}
	else
	if(listRot[i].key == key)
	{
		return false;
	}
	else
	{
		listRot.resize(listRot.size()+1);
		
		for(int j=listRot.size()-1; j>i; j--)
		{
			listRot[j].key = listRot[j-1].key;
			listRot[j].rot = listRot[j-1].rot;
		}
		
		listRot[i].key = key;
		listRot[i].rot = rot;
	}

	if(key > maxKeyRot)		maxKeyRot = key;
	if(key < minKeyRot)		minKeyRot = key;


	return true;
}


///////////////////////////////////////////////////////////////////////////
/// put the animation key for Bone element (already defined)			///
///////////////////////////////////////////////////////////////////////////
bool ZAnim::putAnimBoneKey(ZPRS *prs, float frame, int flag,ZMesh *topo)
{
	if(prs==NULL)		return false;

		
	if((frame<0)||(frame>length))		return false;



	///////////////////////////
	// ----> POSITION <----- //
	///////////////////////////

	if( (flag & ANIM_POS) && (listPos.size()) )
	{
			 if(frame >= maxKeyPos)			prs->SetPos( listPos[listPos.size()-1].pos );
		else if(frame <= minKeyPos)			prs->SetPos( listPos[0].pos );
		else							
		{
			for(int i=0; i<listPos.size() && frame>listPos[i].key; i++);
			
			if(listPos[i].key == frame)		prs->SetPos( listPos[i].pos );
			else
			{

				float t = ((float)(frame-listPos[i-1].key)) / ((float)(listPos[i].key-listPos[i-1].key));
				prs->SetPos( t*listPos[i].pos + (1-t)*listPos[i-1].pos );

			
			}
		}
	}


	///////////////////////////
	// ----> ROTATION <----- //
	///////////////////////////

	if( (flag & ANIM_ANG) && (listRot.size()) )
	{
			 if(frame >= maxKeyRot)			prs->SetAng( listRot[listRot.size()-1].rot.GetAnglesYXZ() );
		else if(frame <= minKeyRot)			prs->SetAng( listRot[0].rot.GetAnglesYXZ() );
		else
		{
			for(int i=0; i<listRot.size() && frame>listRot[i].key; i++);
			
			if(listRot[i].key == frame)		{prs->SetAng( listRot[i].rot.GetAnglesYXZ() );}
			else
			{
				float t = ((float)(frame-listRot[i-1].key)) / ((float)(listRot[i].key-listRot[i-1].key));
				prs->SetAng( (quatSlerp(listRot[i-1].rot, listRot[i].rot, t).GetAnglesYXZ()) );
			}
		}
	}


	return true;
}

///////////////////////////////////////////////////////////////////////////
/// put the animation key (already defined)								///
///////////////////////////////////////////////////////////////////////////
bool ZAnim::putAnimKey(ZPRS *prs, float frame, int flag)
{
	if(prs==NULL)		return false;

		
	if((frame<0)||(frame>length))		return false;


	///////////////////////////
	// ----> POSITION <----- //
	///////////////////////////

	if( (flag & ANIM_POS) && (listPos.size()) )
	{
			 if(frame >= maxKeyPos)			prs->SetPos( listPos[listPos.size()-1].pos );
		else if(frame <= minKeyPos)			prs->SetPos( listPos[0].pos );
		else							
		{
			for(int i=0; i<listPos.size() && frame>listPos[i].key; i++);
			
			if(listPos[i].key == frame)		prs->SetPos( listPos[i].pos );
			else
			{
				float t = ((float)(frame-listPos[i-1].key)) / ((float)(listPos[i].key-listPos[i-1].key));
				prs->SetPos( t*listPos[i].pos + (1-t)*listPos[i-1].pos );
			}
		}
	}


	///////////////////////////
	// ----> ROTATION <----- //
	///////////////////////////

	if( (flag & ANIM_ANG) && (listRot.size()) )
	{
			 if(frame >= maxKeyRot)			prs->SetAng( listRot[listRot.size()-1].rot.GetAnglesYXZ() );
		else if(frame <= minKeyRot)			prs->SetAng( listRot[0].rot.GetAnglesYXZ() );
		else
		{
			for(int i=0; i<listRot.size() && frame>listRot[i].key; i++);
			
			if(listRot[i].key == frame)		prs->SetAng( listRot[i].rot.GetAnglesYXZ() );
			else
			{
				float t = ((float)(frame-listRot[i-1].key)) / ((float)(listRot[i].key-listRot[i-1].key));
				prs->SetAng( (quatSlerp(listRot[i-1].rot, listRot[i].rot, t).GetAnglesYXZ()) );
			}
		}
	}


	return true;
}



///////////////////////////////////////////////////////////////////////////
/// Interpolate between two animation keys								///
///////////////////////////////////////////////////////////////////////////
bool ZAnim::putAnimKeyInterp(ZPRS *prs, float frame0, float frame1, float rate, int flag)
{
	if(prs==NULL)	return false;


	if((frame0<0)||(frame0>length))		return false;
	if((frame1<0)||(frame1>length))		return false;
	

	///////////////////////////
	// ----> POSITION <----- //
	///////////////////////////

	if( (flag & ANIM_POS) && (listPos.size()) )
	{
		ZVector3	pos0, pos1;

		//--- Position 0 ---//

			 if(frame0 >= maxKeyPos)			pos0 = listPos[listPos.size()-1].pos;
		else if(frame0 <= minKeyPos)			pos0 = listPos[0].pos;
		else
		{
			for(int i=0; i<listPos.size() && frame0>listPos[i].key; i++);

			if(listPos[i].key == frame0)		pos0 = listPos[i].pos;
			else
			{
				float t = ((float)(frame0-listPos[i-1].key)) / ((float)(listPos[i].key-listPos[i-1].key));
				pos0 = t*listPos[i].pos + (1-t)*listPos[i-1].pos;
			}
		}

		//--- Position 1 ---//
		
			 if(frame1 >= maxKeyPos)			pos1 = listPos[listPos.size()-1].pos;
		else if(frame1 <= minKeyPos)			pos1 = listPos[0].pos;
		else							
		{
			for(int i=0; i<listPos.size() && frame1>listPos[i].key; i++);

			if(listPos[i].key == frame1)		pos1 = listPos[i].pos;
			else
			{
				float t = ((float)(frame1-listPos[i-1].key)) / ((float)(listPos[i].key-listPos[i-1].key));
				pos1 = t*listPos[i].pos + (1-t)*listPos[i-1].pos;
			}
		}


		// On interpole entre les deux positions au taux de 'rate'
			 if(rate<0)		prs->SetPos( pos0 );
		else if(rate>1)		prs->SetPos( pos1 );
		else prs->SetPos( rate*pos1 + (1-rate)*pos0 );
	}




	///////////////////////////
	// ----> ROTATION <----- //
	///////////////////////////

	if( (flag & ANIM_ANG) && (listRot.size()) )
	{
		ZQuat	rot0, rot1;
		
		//--- Rotation 0 ---//

			 if(frame0 >= maxKeyRot)			rot0 = listRot[listRot.size()-1].rot;
		else if(frame0 <= minKeyRot)			rot0 = listRot[0].rot;
		else
		{
			for(int i=0; i<listRot.size() && frame0>listRot[i].key; i++);

			if(listRot[i].key == frame0)		rot0 = listRot[i].rot;
			else
			{
				float t = ((float)(frame0-listRot[i-1].key)) / ((float)(listRot[i].key-listRot[i-1].key));
				rot0 = quatSlerp(listRot[i-1].rot, listRot[i].rot, t);
			}
		}

		//--- Rotation 1 ---//

			 if(frame1 >= maxKeyRot)			rot1 = listRot[listRot.size()-1].rot;
		else if(frame1 <= minKeyRot)			rot1 = listRot[0].rot;
		else
		{
			for(int i=0; i<listRot.size() && frame1>listRot[i].key; i++);

			if(listRot[i].key == frame1)		rot1 = listRot[i].rot;
			else
			{
				float t = ((float)(frame1-listRot[i-1].key)) / ((float)(listRot[i].key-listRot[i-1].key));
				rot1 = quatSlerp(listRot[i-1].rot, listRot[i].rot, t);
			}
		}


		// Interpolation finale entre les deux quaternions
			 if(rate<0)		prs->SetAng( rot0.GetAnglesYXZ() );
		else if(rate>1)		prs->SetAng( rot1.GetAnglesYXZ() );
		else				prs->SetAng( (quatSlerp(rot0, rot1, rate)).GetAnglesYXZ() );
	}

	return true;
}

