
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_FacialAnimation_FaceAnimation.cpp
// Author : Andy Tate
// Date   : 9-May-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#if defined(ASURA_USE_FACIAL_ANIMATION_SYSTEM)

// This file's header...
#include "FacialAnimation/Asura_FacialAnimation_FaceAnimation.h"

// Asura independent header-files...
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Maths.h"
#include "Debugging/Asura_Logfile.h"
#include "FacialAnimation/Asura_FacialAnimation_MuscleGroupAnimation.h"
#include "FacialAnimation/Asura_FacialAnimation_EyeParamAnimation.h"

#if defined(ASURA_TOOLS)
// Tools header-files...
#include "FacialAnimation/Tools_FacialAnimation_FaceAnimation_FacePose.h"
#include "FacialAnimation/Tools_FacialAnimation_MuscleGroupAnimation.h"
#include "FacialAnimation/Tools_FacialAnimation_System.h"
#endif	//defined(ASURA_TOOLS)


////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

#if 1
#define LOG_OUTPUT(s)
#else
#define LOG_OUTPUT(s)	Asura_OutputToDebugger_Retail(s)
#endif


////////////////////////////////////////////////////////////////////////////////
// Version	Author	Comment
// ~~~~~~~	~~~~~~	~~~~~~~
// 1		ART		Created.
// 2		RAR		Added eye animation. 15/10/2008
////////////////////////////////////////////////////////////////////////////////
const u_int	Asura_FacialAnimation_FaceAnimation::s_uChunkVersion	= 2;


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::CreateMuscleGroupAnimations
// Author       : Andy Tate
// Date         : 28-Aug-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_MuscleGroupAnimation* Asura_FacialAnimation_FaceAnimation::CreateMuscleGroupAnimations(const u_int uNumMuscleGroups)
{
	delete[](m_pxMuscleGroupAnimations);

	m_uNumMuscleGroups			= uNumMuscleGroups;
	m_pxMuscleGroupAnimations	= new Asura_FacialAnimation_MuscleGroupAnimation [uNumMuscleGroups];

	return(m_pxMuscleGroupAnimations);
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::CreateMuscleGroupAnimations'. ///



////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Asura_FacialAnimation_FaceAnimation
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_FaceAnimation::Asura_FacialAnimation_FaceAnimation(void)
: m_uFaceTypeHash( ASURA_HASH_ID_UNSET )
, m_fDuration(0.0f)
, m_uNumMuscleGroups(0)
, m_pxMuscleGroupAnimations(NULL)
, m_pxEyeParamAnimations(NULL)
{
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Asura_FacialAnimation_FaceAnimation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Asura_FacialAnimation_FaceAnimation
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_FaceAnimation::Asura_FacialAnimation_FaceAnimation(const Asura_Hash_ID uHash,
																		 Asura_Chunk_Stream& xStream)
: m_uFaceTypeHash( ASURA_HASH_ID_UNSET )
, m_fDuration(0.0f)
, m_uNumMuscleGroups(0)
, m_pxMuscleGroupAnimations(NULL)
, m_pxEyeParamAnimations(NULL)
{
	ReadFromChunkStream(xStream);
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Asura_FacialAnimation_FaceAnimation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::~Asura_FacialAnimation_FaceAnimation
// Author       : Andy Tate
// Date         : 28-Aug-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_FaceAnimation::~Asura_FacialAnimation_FaceAnimation()
{
	delete[](m_pxMuscleGroupAnimations);
	delete[](m_pxEyeParamAnimations);
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::~Asura_FacialAnimation_FaceAnimation'. ///



#if defined(ASURA_TOOLS)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Tools_InitialiseFromFaceType
// Author       : Andy Tate
// Date         : 29-Aug-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimation::Tools_InitialiseFromFaceType(const Asura_String& xFaceTypeName)
{
	const Tools_FacialAnimation_FaceType*	pxToolsFaceType	= Tools_FacialAnimation_System::FindOrLoadType(xFaceTypeName);

	m_uFaceTypeHash = xFaceTypeName.GetHash();

	if(pxToolsFaceType == NULL)
	{
		Asura_Assert(false, "Cannot find the face-type to use when creating this animation.");
	}
	else
	{
		const u_int	uNumMuscleGroups	= pxToolsFaceType->GetNumMuscleGroups();

		CreateMuscleGroupAnimations(uNumMuscleGroups);
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Tools_InitialiseFromFaceType'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Tools_CreateFromMuscleGroupAnimation
// Author       : Richard Rice
// Date         : 23-Sep-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimation::Tools_CreateFromMuscleGroupAnimation(const Tools_FacialAnimation_MuscleGroupAnimation& xToolsMuscleGroupAnimation,
																			const Tools_FacialAnimation_FaceType& xToolsFaceType)
{

	// Take each of the animated weightings and create and Asura-side equivalent...
	const Asura_Hash_ID	uMuscleGroupHash	= xToolsMuscleGroupAnimation.GetHashID();
	const int			iMuscleGroupIndex	= xToolsFaceType.FindMuscleGroupIndex(uMuscleGroupHash);

	if(iMuscleGroupIndex >= 0)
	{
		m_pxMuscleGroupAnimations[iMuscleGroupIndex].Tools_SetKeyFrames(xToolsMuscleGroupAnimation);

		float fDuration = m_pxMuscleGroupAnimations[iMuscleGroupIndex].Tools_GetLastKeyFrameTime();
		if(fDuration > m_fDuration)
		{
			m_fDuration = fDuration;
		}
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Tools_CreateFromMuscleGroupAnimation'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Tools_CopyEyeAnimation
// Author       : Richard Rice
// Date         : 15-Oct-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimation::Tools_CopyEyeAnimation(const Asura_FacialAnimation_EyeParamAnimation* pxEyeParamAnimations)
{
	Asura_Assert(pxEyeParamAnimations, "");

	delete [] m_pxEyeParamAnimations;
	m_pxEyeParamAnimations = new Asura_FacialAnimation_EyeParamAnimation[ASURA_FACIALANIMATION_EYE_NUMPARAM];

	for(int i=0; i<ASURA_FACIALANIMATION_EYE_NUMPARAM; i++)
	{
		m_pxEyeParamAnimations[i].Tools_SetKeyFrames(pxEyeParamAnimations[i]);
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Tools_CopyEyeAnimation'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Tools_Rationalise
// Author       : Andy Tate
// Date         : 29-Aug-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimation::Tools_Rationalise(void)
{
	for(u_int u = 0; u < m_uNumMuscleGroups; u++)
	{
		LOG_OUTPUT(("-"));
		m_pxMuscleGroupAnimations[u].Tools_RationaliseKeyFrames();
		LOG_OUTPUT(("\n"));
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Tools_Rationalise'. ///

#endif	//defined(ASURA_TOOLS)



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::Update
// Author       : Andy Tate
// Date         : 27-Aug-2008
////////////////////////////////////////////////////////////////////////////////

bool Asura_FacialAnimation_FaceAnimation::Update(const float fTime,
												 float* pfWeights,
												 float* pfBalances,
												 float* pfEyeValues) const
{
	if(fTime <= m_fDuration)
	{
		const Asura_FacialAnimation_MuscleGroupAnimation*	pxMuscleGroupAnimation	= m_pxMuscleGroupAnimations;

		for(u_int u = 0; u < m_uNumMuscleGroups; u++, pxMuscleGroupAnimation++)
		{
			const float	fAbsWeight	= Asura_Maths::Abs(pfWeights[u]);
			const float	fAbsBalance	= Asura_Maths::Abs(pfBalances[u]);

			float		fNewWeight;
			float		fNewBalance;

			if(pxMuscleGroupAnimation->CalculateWeightAndBalance(fTime, fNewWeight, fNewBalance))
			{
				// For the time being pick the largest of the weights and balances...
				if(Asura_Maths::Abs(fNewWeight) > fAbsWeight)
				{
					pfWeights[u]	= fNewWeight;
				}
				if(Asura_Maths::Abs(fNewBalance) > fAbsBalance)
				{
					pfBalances[u]	= fNewBalance;
				}
			}
		}
		if(pfEyeValues && m_pxEyeParamAnimations)
		{
			for(u_int i=0; i<ASURA_FACIALANIMATION_EYE_NUMPARAM; i++)
			{
				float fNewValue;
				if(m_pxEyeParamAnimations[i].CalculateValue(fTime, fNewValue))
				{
					if( i==ASURA_FACIALANIMATION_EYE_TARGETMIX || i==ASURA_FACIALANIMATION_EYE_SQUINT )
					{
						//for target mix and squint, go for the lower value
						if(Asura_Maths::Abs(fNewValue) < Asura_Maths::Abs(pfEyeValues[i]))
						{
							pfEyeValues[i] = fNewValue;
						}
					}
					else
					{
						if(Asura_Maths::Abs(fNewValue) > Asura_Maths::Abs(pfEyeValues[i]))
						{
							pfEyeValues[i] = fNewValue;
						}
					}
				}
			}
		}
		return(true);
	}
	else
	{
		return(false);
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::Update'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::ReadFromChunkStream
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimation::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int	uVersion;

	// First, read (and verify) the chunk-version.
	xStream >> uVersion;

	switch(uVersion)
	{
		case 1:
		{
			xStream >> m_fDuration;
			xStream >> m_uNumMuscleGroups;

			CreateMuscleGroupAnimations(m_uNumMuscleGroups);

			for(u_int u = 0; u < m_uNumMuscleGroups; u++)
			{
				m_pxMuscleGroupAnimations[u].ReadFromChunkStream(xStream);
			}
			break;
		}

		case 2:
		{
			xStream >> m_fDuration;
			xStream >> m_uNumMuscleGroups;

			CreateMuscleGroupAnimations(m_uNumMuscleGroups);

			for(u_int u = 0; u < m_uNumMuscleGroups; u++)
			{
				m_pxMuscleGroupAnimations[u].ReadFromChunkStream(xStream);
			}

			//Any eye animation saved?
			int iNumEyeParam;
			xStream >> iNumEyeParam;
			if(iNumEyeParam)
			{
				//should always be the same number of values being animated for this
				Asura_Assert(iNumEyeParam == ASURA_FACIALANIMATION_EYE_NUMPARAM, "Wrong number of animations for eye parameters");
				m_pxEyeParamAnimations = new Asura_FacialAnimation_EyeParamAnimation[ASURA_FACIALANIMATION_EYE_NUMPARAM];
				
				for(int i=0; i<ASURA_FACIALANIMATION_EYE_NUMPARAM;i++)
				{
					m_pxEyeParamAnimations[i].ReadFromChunkStream(xStream);
				}
			}

			break;
		}


		default:
		{
			Asura_Assertf(false, ("Unexpected chunk-version %d", uVersion));
			break;
		}
	}		// End of switch.
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::ReadFromChunkStream'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimation::WriteToChunkStream
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimation::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	// First write the chunk-version...
	xStream << s_uChunkVersion;
	
	xStream << m_fDuration;
	xStream << m_uNumMuscleGroups;

	for(u_int u = 0; u < m_uNumMuscleGroups; u++)
	{
		m_pxMuscleGroupAnimations[u].WriteToChunkStream(xStream);
	}

	if(m_pxEyeParamAnimations)
	{
		xStream << (int) ASURA_FACIALANIMATION_EYE_NUMPARAM;
		for(int i=0; i<ASURA_FACIALANIMATION_EYE_NUMPARAM; i++)
		{
			m_pxEyeParamAnimations[i].WriteToChunkStream(xStream);
		}
	}
	else
	{
		xStream << 0;
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimation::WriteToChunkStream'. ///

#endif	//defined(ASURA_USE_FACIAL_ANIMATION_SYSTEM)


/// End of file. ///
