
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_AnimationController_Animation.cpp
// Author : Andy Tate
// Date   : 14-Sep-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Animation/Asura_AnimationController_Animation.h"

// Asura independent header-files...
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationBlend.h"
#include "Animation/Asura_Hierarchy.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
#include "Debugging/Asura_DebugText.h"

// Asura platform header-files...

// Game independent header-files...

// Game platform header-files...

// Tools header-files...

#if (defined ASURA_USE_DEBUG_TEXT || defined ASURA_LOGFILES_ENABLED)
static const char* s_aszStateString[] =
{
	"INVALID",
	"PAUSED",
	"PLAYING",
	"FINISHED"
};
#endif

////////////////////////////////////////////////////////////////////////////////
// Defines...
////////////////////////////////////////////////////////////////////////////////

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


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


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


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


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

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::InitialiseAnimation
// Author       : Andy Tate
// Date         : 15-Sep-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationController_Animation::InitialiseAnimation (void)
{
	// Get the appropriate animation data...
	Asura_Assert((m_pxAnimation == NULL), "We already have an animation - this will be overridden by the one we're initialising.");

	// Is this the rest-pose?
	if(m_uAnimationHash == ASURA_HASH_ID_UNSET)
	{
		// Yes - make sure this is handled elsewhere...
		m_fDuration			= 0.0f;
		m_fOneOverDuration	= 0.0f;
		m_bIsDeltaAnimation	= false;
	}
	else
	{
		m_pxAnimation	= Asura_Animation_System::GetAnimByHash(m_uAnimationHash);

		if(m_pxAnimation == NULL)
		{
			Asura_Assertf(false, ("Failed to find animation with hash-ID %08X", m_uAnimationHash));
			return(false);
		}
		else
		{
			// Initialise the timers...
			m_bIsDeltaAnimation	= m_pxAnimation->IsDeltaAnimation;
			m_fDuration			= m_pxAnimation->Duration;
			m_fOneOverDuration	= (m_fDuration > 0.f) ? (1.0f / m_fDuration) : 0.f;

#if defined(ASURA_USE_KEYFRAME_CACHING)
			// Initialise key-frame caching...
			delete[](m_pxCachedKeyFrames);
			m_pxCachedKeyFrames	= new Asura_Cached_KeyFrame [m_pxAnimation->GetNumberOfBones()];
#endif	// defined(ASURA_USE_KEYFRAME_CACHING)
		}
	}

	m_fTimeDelay	= 0.0f;

	// Initialise the remaining variables...
	m_fParametricStartTime		= 0.0f;
	m_fParametricEndTime		= 1.0f;
	m_fParametricTimer			= 0.0f;
	m_fParametricTargetTime		= 0.0f;
	m_bUseParametricTargetTime	= false;

	m_fTimeUntilFinished	= (m_uControllerFlags & AACA_FLAG_LOOPED) ? Asura_Maths::LargeFloat : m_fDuration;
	m_fAnimPlayed			= ((m_uControllerFlags & AACA_FLAG_REVERSED) == 0) ? 0.0f : 1.0f;
	m_fOldAnimPlayed		= m_fAnimPlayed;

	// Return success...
	return(true);
}

/// End of function 'Asura_AnimationController_Animation::InitialiseAnimation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::ChangeState
// Author       : Andy Tate
// Date         : 15-Sep-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationController_Animation::ChangeState (const State eState)
{
	// What are we trying to do?
	switch(eState)
	{
		case PAUSED:
		{
			switch(m_eNewState)
			{
				case INVALID:
				{
					// Are we able to do this from the state we're currently in?
					if(m_eCurrentState == PLAYING)
					{
						m_eNewState	= eState;
						return(true);
					}
					else
					{
						Asura_Assert(false, "Incorrect state for animation instance to be paused from.");
						return(false);
					}
				}

				case PLAYING:
				case PAUSED:
				{
					m_eNewState	= eState;
					return(true);
				}

				default:
				{
					Asura_Assert(false, "Cannot change from new state to paused.");
					return(false);
				}
			}		// End of switch.
		}

		case PLAYING:
		{
			switch(m_eNewState)
			{
				case INVALID:
				{
					// Are we able to do this from the state we're currently in?
					if((m_eCurrentState == PAUSED) || (m_eCurrentState == FINISHED))
					{
						m_eNewState	= eState;
						return(true);
					}
					else
					{
						Asura_Assert(false, "Incorrect state for animation instance to be played from.");
						return(false);
					}
				}

				case PAUSED:
				case PLAYING:
				case FINISHED:
				{
					m_eNewState	= eState;
					return(true);
				}

				default:
				{
					Asura_Assert(false, "Cannot change from new state to playing.");
					return(false);
				}
			}		// End of switch.
		}

		case FINISHED:
		{
			switch(m_eNewState)
			{
				case INVALID:
				{
					m_eNewState	= eState;
					return(true);
				}

				case PAUSED:
				case PLAYING:
				case FINISHED:
				{
					m_eNewState	= eState;
					return(true);
				}

				default:
				{
					Asura_Assert(false, "Cannot change from new state to finished.");
					return(false);
				}
			}		// End of switch.
		}

		default:
		{
			Asura_Assert(false, "Unexpected state to change to.");
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_AnimationController_Animation::ChangeState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::CalculateParametricTime
// Author       : Andy Tate
// Date         : 20-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::UpdateAnimPlayed (void)
{
	float fClampedTime	= Asura_Maths::ClampToRange(m_fParametricTimer, m_fParametricStartTime, m_fParametricEndTime);
	if(m_bUseParametricTargetTime && fClampedTime > m_fParametricTargetTime)
	{
		fClampedTime = m_fParametricTargetTime;
	}

	// Is the animation reversed?
	if((m_uControllerFlags & AACA_FLAG_REVERSED) == 0)
	{
		m_fOldAnimPlayed	= m_fAnimPlayed;
		m_fAnimPlayed		= fClampedTime;
	}
	else
	{
		m_fOldAnimPlayed	= m_fAnimPlayed;
		m_fAnimPlayed		= 1.0f - fClampedTime;
	}

	// Calculate the time until we're finished...
	if(m_bUseParametricTargetTime)
	{
		if(m_fParametricStartTime == 0.0f && m_fParametricEndTime == 1.0f)
		{
			m_fTimeUntilFinished = (m_fParametricTargetTime - m_fAnimPlayed)*m_fDuration;
		}
		else
		{
			//Bit more of a pain to get the time left if the start or end time is being used.
			//(Not sure it is useful to use them in conjunction with the target time anyway)
			//RAR 15/08/2007
			float fDistanceToGo = 0.0f;
			if(m_fParametricTargetTime < m_fParametricEndTime)
			{
				//before the end time/loop point , so its nice and easy
				fDistanceToGo = m_fParametricTargetTime - m_fAnimPlayed;
			}
			else
			{
				//get the distance for the rest of this loop
				fDistanceToGo = m_fParametricEndTime - m_fAnimPlayed;

				//get the distance for anu subsequent complete loops
				float fLoops = (float)Asura_Maths::FloatToInt_Chop(m_fParametricTargetTime);
				if(fLoops > 1.0f)
				{
					fDistanceToGo += (fLoops-1.0f) * (m_fParametricEndTime - m_fParametricStartTime);
				}

				//get any remaining time from the final parial loop
				float fModifiedTargetTime = m_fParametricTargetTime - fLoops;
				fDistanceToGo += Asura_Maths::ClampToRange(fModifiedTargetTime - m_fParametricStartTime, 0.0f, m_fParametricEndTime - m_fParametricStartTime);
			}

			m_fTimeUntilFinished = fDistanceToGo * m_fDuration;

		}
		if(m_fTimeUntilFinished < 0.0f) m_fTimeUntilFinished = 0.0f;
	}
	else
	{
		if ((m_uControllerFlags & AACA_FLAG_LOOPED) != 0)
		{
			m_fTimeUntilFinished = Asura_Maths::LargeFloat;
		}
		else
		{
			m_fTimeUntilFinished = m_fDuration * (1.0f - m_fAnimPlayed);
			if ((m_uControllerFlags & AACA_FLAG_REVERSED) != 0)
			{
				m_fTimeUntilFinished = m_fDuration - m_fTimeUntilFinished;
			}
		}
	}
}

/// End of function 'Asura_AnimationController_Animation::CalculateParametricTime'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::CalculateBoundingBox
// Author       : Andy Tate
// Date         : 21-Sep-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationController_Animation::CalculateBoundingBox (const Asura_Hierarchy_Skin& xSkin,
																const Asura_Hierarchy_Tween& xTweenStore,
																Asura_Bounding_Box& xBB) const
{
	const Asura_Bounding_Box*	pxAnimationBB	= (m_pxAnimation == NULL) ? NULL : m_pxAnimation->GetBB();

	// Update the bounding box...
	if(pxAnimationBB == NULL)
	{
		const Asura_Hierarchy_TweenStore_BoneData&	xBoneData	= xTweenStore.GetBoneData(0);

#if defined(ASURA_HIERARCHY_QUAD)
		Asura_AMatrix4x4							xRootTransform;
		Asura_AMatrix4x4							xTransform;

		Asura_Vector_3								xDisplacement;
		Asura_Matrix_3x3							xOrientation;

		// RJT TODO: Optimise "Asura_Bounding_Box" to use "Asura_AMatrix4x4"...
		xBoneData.GetTransform(xRootTransform);
		xRootTransform.Apply(xSkin.m_pxBoneRestPoseInverseTransforms[0], xTransform);

		xDisplacement = xTransform.GetTranslation();
		xTransform.ToMatrix(xOrientation);

		xBB.FromOBB(xSkin.GetBB(), xOrientation);
		xBB.Displace(xDisplacement);
#else
		Asura_Bounding_Box	xTempBB1(xSkin.GetBB());
		Asura_Vector_3		xRootPosition;
		Asura_Matrix_3x3	xRootOrientation;
		Asura_Bounding_Box	xTempBB2;

		xBoneData.GetOrientation(xRootOrientation);
		xBoneData.GetPosition(xRootPosition);

		//RAR 08/04/03 - Take into account animations that move the character from the compass point for culling
		//And offset it by the movement of the first bone relative to the rest pose position.
		//(the bounding box centre should move by roughly the same amount)
		xTempBB1.Displace(-xSkin.BoneRestPosition[0]);
		xTempBB2.FromOBB(xTempBB1, xSkin.BoneRestOrientation[0]);
		xBB.FromOBB(xTempBB2, xRootOrientation);
		xBB.Displace(xRootPosition);
#endif	// defined(ASURA_HIERARCHY_QUAD)
	}
	else
	{
		xBB	= *pxAnimationBB;
	}

	return(true);
}

/// End of function 'Asura_AnimationController_Animation::CalculateBoundingBox'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::CalculateBoneRelativeData
// Author       : Andy Tate
// Date         : 15-Sep-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationController_Animation::CalculateBoneRelativeData (const Asura_Hierarchy_Skin& xSkin,
																	 const u_int uBone,
																	 Asura_Hierarchy_Bone_RestFrame& xOutput) const
{
	const bool	bMirrored	= ((m_uControllerFlags & AACA_FLAG_MIRRORED) != 0);
	const u_int	uActualBone	= (bMirrored) ? xSkin.GetMirroredBone(uBone) : uBone;

	// Do we have an animation?
	if(m_pxAnimation == NULL)
	{
		if ( m_bIsDeltaAnimation )
		{
			// Delta - don't use this bone...
			return(false);
		}
		else
		{
			// Normal animation - use the rest-pose...
			xOutput	= xSkin.BoneRestPose[uActualBone];

			// Mirror the position being tweened to if needed.
			// (The tween storage itself will be mirrored as required already).
			// RAR 23/05/2006
			if(bMirrored)
			{
				xSkin.ApplyMirroring(uActualBone, false, xOutput.Quat, xOutput.Position);
			}

			return(true);
		}
	}
	else
	{
		const u_int	uNumKeyFrames	= m_pxAnimation->GetNumberOfKeyFrames(uActualBone);

		// Do we have any key-frames for this bone?
		if(uNumKeyFrames == 0)
		{
			return(false);
		}
		else
		{
			// Deal with any special cases...
#if defined(ASURA_USE_KEYFRAME_CACHING)
			if(m_pxCachedKeyFrames != NULL)
			{
				m_pxAnimation->GetRelativeBonePosition(uActualBone, m_fAnimPlayed, &xOutput.Position, &xOutput.Quat, &m_pxCachedKeyFrames[uActualBone]);
			}
			else
#endif	// defined(ASURA_USE_KEYFRAME_CACHING)
			{
				if(m_fAnimPlayed == 0.0f)
				{
					m_pxAnimation->GetKeyFrameQuat(xOutput.Quat, uActualBone, 0);
					xOutput.Position	= m_pxAnimation->GetKeyFramePosition(uActualBone, 0);
				}
				else
				{
					m_pxAnimation->GetRelativeBonePosition(uActualBone, m_fAnimPlayed, &xOutput.Position, &xOutput.Quat);
				}
			}

			// Do we need to modify the position?
			if(m_pxAnimation->BoneLengths != NULL)
			{
				const float	fBoneLength	= m_pxAnimation->BoneLengths[uActualBone];

				// Do we have a valid bone-length in the animation?
				if(fBoneLength > 0.01f)
				{
					const Asura_Vector_3&	xBoneRestPosition	= xSkin.BoneRestPose[uActualBone].Position;
					const float				fBoneSize			= xBoneRestPosition.Magnitude();
					const float				fScale				= fBoneSize / fBoneLength;

					// Use the bone length from the skin instead of the animation...
					xOutput.Position	*= fScale;
				}
			}

			if(m_pxAnimation->m_bSharedAnimDeltaPos)
			{
				//Animation is done as deltas relative to the skin rest pose , so multiply back in the rest pose.
				//RAR 05/02/2007

				if(m_pxAnimation->m_bSharedAnimDeltaOrient)
				{
					xOutput.Quat = xSkin.BoneRestPose[uActualBone].Quat*xOutput.Quat;
					xOutput.Position = xSkin.BoneRestPose[uActualBone].Quat.RotateVector(xOutput.Position);
				}
				xOutput.Position += xSkin.BoneRestPose[uActualBone].Position;
			}

			// Mirror the position being tweened to if needed.
			// (The tween storage itself will be mirrored as required already).
			// RAR 23/05/2006
			if(bMirrored)
			{
				xSkin.ApplyMirroring(uActualBone, m_pxAnimation->IsDeltaAnimation, xOutput.Quat, xOutput.Position);
			}

			return(true);
		}
	}
}

/// End of function 'Asura_AnimationController_Animation::CalculateBoneRelativeData'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::IntervalIntersects
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationController_Animation::IntervalIntersects (const float fStart1,
															  const float fEnd1,
															  const float fStart2,
															  const float fEnd2)
{
	// Is our second interval within the first one?
	if( ((fStart2 >= fStart1) && (fStart2 <= fEnd1)) ||
		((fEnd2 >= fStart1) && (fEnd2 <= fEnd1)) )
	{
		return(true);
	}
	else
	{
		// No - is the first interval within the second one?
		if( ((fStart1 >= fStart2) && (fStart1 <= fEnd2)) ||
			((fEnd1 >= fStart2) && (fEnd1 <= fEnd2)) )
		{
			return(true);
		}
		else
		{
			return(false);
		}
	}
}

/// End of function 'Asura_AnimationController_Animation::IntervalIntersects'. ///



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

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::Asura_AnimationController_Animation
// Author       : Andy Tate
// Date         : 15-Sep-2006
////////////////////////////////////////////////////////////////////////////////

Asura_AnimationController_Animation::Asura_AnimationController_Animation (const Asura_Hash_ID uControllerHash,
																		  const u_int uInitialFlags,
																		  const u_int uPriority,
																		  const Asura_Hash_ID uAnimationHash)
: Asura_AnimationController(uControllerHash, uInitialFlags, uPriority)
, m_uAnimationHash(uAnimationHash)
, m_pxAnimation(NULL)
, m_fDuration(0.0f)
, m_fOneOverDuration(0.0f)
, m_fTimeDelay(0.0f)
, m_fParametricStartTime(0.0f)
, m_fParametricEndTime(1.0f)
, m_fParametricTimer(0.0f)
, m_fParametricTargetTime(0.0f)
, m_fAnimPlayed(0.0f)
, m_fOldAnimPlayed(0.0f)
, m_fTimeUntilFinished(0.0f)
#if defined(ASURA_USE_KEYFRAME_CACHING)
, m_pxCachedKeyFrames(NULL)
#endif	// defined(ASURA_USE_KEYFRAME_CACHING)
, m_uControllerAnimationFlags(0)
{
	// Initialise the states...
	m_eCurrentState	= PAUSED;
	m_eNewState		= INVALID;

	// Initialise the animation itself...
	InitialiseAnimation();
}

/// End of function 'Asura_AnimationController_Animation::Asura_AnimationController_Animation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::~Asura_AnimationController_Animation
// Author       : Andy Tate
// Date         : 18-Dec-2006
////////////////////////////////////////////////////////////////////////////////

Asura_AnimationController_Animation::~Asura_AnimationController_Animation ()
{
#if defined(ASURA_USE_KEYFRAME_CACHING)
	// Remove the cached key-frames...
	delete[](m_pxCachedKeyFrames);
#endif	// defined(ASURA_USE_KEYFRAME_CACHING)

	// Remove from the correct hash-tree...
	m_pxAsuraAnimation->m_xControllerAnimationContainer.DeleteNodeByHash(m_uControllerHash);
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::TweenBoundingBox
// Author       : Andy Tate
// Date         : 21-Sep-2006
////////////////////////////////////////////////////////////////////////////////

u_int Asura_AnimationController_Animation::TweenBoundingBox(Asura_Bounding_Box& xBB,
															const Asura_Hierarchy_Skin& xSkin,
															const float fWeight,
															const bool bDeltasUseBlends,
															const bool bSlerpVelocity,
															Asura_AnimationController_Group* pxParentGroup)
{
	const bool						bReversed					= ((m_uControllerFlags & AACA_FLAG_REVERSED) != 0);
	const bool						bMirrored					= ((m_uControllerFlags & AACA_FLAG_MIRRORED) != 0);

	Asura_Hierarchy_Tween&			xToTweenStore				= pxParentGroup->GetTweenStore();
	const bool						bBlendBoneData				= (m_bIsDeltaAnimation)
																? ((xToTweenStore.IsBoneDataDelta(0)) ?  bDeltasUseBlends : false)
																: true;

	Asura_Hierarchy_Bone_RestFrame	xTempRestFrame;

	// Is this animation a delta?
	if(m_pxAnimation != NULL)
	{
		if( !m_bIsDeltaAnimation && m_pxAnimation->GetBB() )
		{
			xBB.Set( *m_pxAnimation->GetBB() );
		}

		if(bBlendBoneData)
		{
			// Blend the movement-data...
			if((m_uControllerFlags & AACA_FLAG_CALCULATE_VELOCITY) != 0)
			{
				Asura_Vector_3	xVelocity;
				const bool bDontTweenVelocity = ( m_uControllerFlags & AACA_FLAG_DONT_TWEEN_VELOCITY ) != 0;

				const float fAnimPlayed = GetAdjustedAnimPlayed();

				if(m_pxAnimation->GetVelocity(&xSkin, fAnimPlayed, m_fDuration, &xVelocity, bMirrored, bReversed))
				{
					xToTweenStore.BlendVelocity(xVelocity, m_bIsDeltaAnimation, bDontTweenVelocity ? 1.0f : fWeight, bSlerpVelocity);
				}
				else
				{
					xToTweenStore.BlendEmptyVelocity(bDontTweenVelocity ? 1.0f : fWeight);
				}
			}

			if((m_uControllerFlags & AACA_FLAG_CALCULATE_ANGULAR_VELOCITY) != 0)
			{
				Asura_Vector_3	xAngularVelocityAxis;
				float			fAngularVelocityAngle;
				const bool bDontTweenAngularVelocity = ( m_uControllerFlags & AACA_FLAG_DONT_TWEEN_ANGULAR_VELOCITY ) != 0;

				const float fAnimPlayed = GetAdjustedAnimPlayed();

				if(m_pxAnimation->GetAngularVelocity(fAnimPlayed, m_fDuration, &xAngularVelocityAxis, &fAngularVelocityAngle, bMirrored, bReversed))
				{
					xToTweenStore.BlendAngularVelocity(xAngularVelocityAxis, fAngularVelocityAngle, m_bIsDeltaAnimation, bDontTweenAngularVelocity ? 1.0f : fWeight);
				}
				else
				{
					xToTweenStore.BlendEmptyAngularVelocity(bDontTweenAngularVelocity ? 1.0f : fWeight);
				}
			}

			if((m_uControllerFlags & AACA_FLAG_CALCULATE_MOVEMENT_OFFSET) != 0)
			{
				Asura_Vector_3	xMovementOffsetPosition;
				Asura_Quat		xMovementOffsetQuat;
				const bool bDontTweenMovementOffset = ( m_uControllerFlags & AACA_FLAG_DONT_TWEEN_MOVEMENT_OFFSET ) != 0;

				if(m_pxAnimation->GetMovementOffset(&xSkin,m_fAnimPlayed, &xMovementOffsetPosition, &xMovementOffsetQuat, bMirrored))
				{
					xToTweenStore.BlendMovementOffset(xMovementOffsetPosition, xMovementOffsetQuat, m_bIsDeltaAnimation, bDontTweenMovementOffset ? 1.0f : fWeight);
				}
				else
				{
					xToTweenStore.BlendEmptyMovementOffset(bDontTweenMovementOffset ? 1.0f : fWeight);
				}
			}
		}
		else
		{
			// Blend the movement-data...
			if((m_uControllerFlags & AACA_FLAG_CALCULATE_VELOCITY) != 0)
			{
				Asura_Vector_3	xVelocity;

				if(m_pxAnimation->GetVelocity(&xSkin,m_fAnimPlayed, m_fDuration, &xVelocity, bMirrored, bReversed))
				{
					const bool bDontTweenVelocity = ( m_uControllerFlags & AACA_FLAG_DONT_TWEEN_VELOCITY ) != 0;
					xToTweenStore.AddVelocity(xVelocity, bDontTweenVelocity ? 1.0f : fWeight);
				}
			}

			if((m_uControllerFlags & AACA_FLAG_CALCULATE_ANGULAR_VELOCITY) != 0)
			{
				Asura_Vector_3	xAngularVelocityAxis;
				float			fAngularVelocityAngle;

				if(m_pxAnimation->GetAngularVelocity(m_fAnimPlayed, m_fDuration, &xAngularVelocityAxis, &fAngularVelocityAngle, bMirrored, bReversed))
				{
					const bool bDontTweenAngularVelocity = ( m_uControllerFlags & AACA_FLAG_DONT_TWEEN_ANGULAR_VELOCITY ) != 0;
					xToTweenStore.AddAngularVelocity(xAngularVelocityAxis, fAngularVelocityAngle, bDontTweenAngularVelocity ? 1.0f : fWeight);
				}
			}

			if((m_uControllerFlags & AACA_FLAG_CALCULATE_MOVEMENT_OFFSET) != 0)
			{
				Asura_Vector_3	xMovementOffsetPosition;
				Asura_Quat		xMovementOffsetQuat;

				if(m_pxAnimation->GetMovementOffset(&xSkin,m_fAnimPlayed, &xMovementOffsetPosition, &xMovementOffsetQuat, bMirrored))
				{
					const bool bDontTweenMovementOffset = ( m_uControllerFlags & AACA_FLAG_DONT_TWEEN_MOVEMENT_OFFSET ) != 0;
					xToTweenStore.AddMovementOffset(xMovementOffsetPosition, xMovementOffsetQuat, bDontTweenMovementOffset ? 1.0f : fWeight);
				}
			}
		}
	}

	Asura_Assertf( !xToTweenStore.IsBlendError(), ( "Error blending animation %s in controller 0x%08x into group 0x%08x", GetAnimation() ? GetAnimation()->GetName() : "Error", GetControllerHash(), pxParentGroup->GetControllerHash() ) );

	// Return the number of controllers updated - always 1 for animations...
	return(1);
}

/// End of function 'Asura_AnimationController_Animation::TweenBoundingBox'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::TweenBoneData
// Author       : Andy Tate
// Date         : 15-Sep-2006
////////////////////////////////////////////////////////////////////////////////

u_int Asura_AnimationController_Animation::TweenBoneData(const Asura_Hierarchy_Skin& xSkin,
														 const u_int uFirstBone,
														 const u_int uLastBone,
														 const float fWeight,
														 const bool bDeltasUseBlends,
														 Asura_AnimationController_Group* pxParentGroup)
{
	const bool						bDontTweenRootOrientation	= ((m_uControllerFlags & AACA_FLAG_DONT_TWEEN_ROOT_ORIENTATION) != 0);
	const bool						bDontTweenRootPosition		= ((m_uControllerFlags & AACA_FLAG_DONT_TWEEN_ROOT_POSITION) != 0);

	Asura_Hierarchy_Tween&			xToTweenStore				= pxParentGroup->GetTweenStore();
	u_int							uLastValidBone				= (m_pxAnimation == NULL)
																? uLastBone
																: Asura_Maths::Min(uLastBone,  static_cast<u_int>(m_pxAnimation->NumberOfBones) - 1);

	Asura_Hierarchy_Bone_RestFrame	xTempRestFrame;

	// Pre-fetch the first bone...
	xToTweenStore.PreFetchBoneData(uFirstBone);

	// Go through each bone in-turn...
	for(u_int uBone = uFirstBone; uBone <= uLastValidBone; uBone++)
	{
		// Pre-fetch the next bone...
		if(uBone < uLastValidBone)
		{
			xToTweenStore.PreFetchBoneData(uBone + 1);
		}

		// Calculate the data to use...
		if(CalculateBoneRelativeData(xSkin, uBone, xTempRestFrame))
		{				
			const bool	bBlendBoneData	= (m_bIsDeltaAnimation)
										? ((xToTweenStore.IsBoneDataDelta(uBone)) ? bDeltasUseBlends : false)
										: true;

			// Is this animation a delta?
			if(bBlendBoneData)
			{
				if(bDontTweenRootOrientation && (uBone == 0))
				{
					xToTweenStore.BlendBoneData(uBone, xTempRestFrame.Quat, m_bIsDeltaAnimation);
				}
				else
				{
					xToTweenStore.BlendBoneData(uBone, xTempRestFrame.Quat, m_bIsDeltaAnimation, fWeight);
				}

				if(bDontTweenRootPosition && (uBone == 0))
				{
					xToTweenStore.BlendBoneData(uBone, xTempRestFrame.Position, m_bIsDeltaAnimation);
				}
				else
				{
					xToTweenStore.BlendBoneData(uBone, xTempRestFrame.Position, m_bIsDeltaAnimation, fWeight);
				}
			}
			else
			{
				// This needs to be done before the orientation , so the right rotation
				// gets applied to it
				// RAR 16/11/2007
				if(bDontTweenRootPosition && (uBone == 0))
				{
					xToTweenStore.AddBoneData(uBone, xTempRestFrame.Position);
				}
				else
				{
					xToTweenStore.AddBoneData(uBone, xTempRestFrame.Position, fWeight);
				}

				if((uBone == 0) && bDontTweenRootOrientation)
				{
					xToTweenStore.AddBoneData(uBone, xTempRestFrame.Quat);
				}
				else
				{
					xToTweenStore.AddBoneData(uBone, xTempRestFrame.Quat, fWeight);
				}
			}
		}
	}

	Asura_Assertf( !xToTweenStore.IsBlendError(), ( "Error blending animation %s in controller 0x%08x into group 0x%08x", GetAnimation() ? GetAnimation()->GetName() : "Error", GetControllerHash(), pxParentGroup->GetControllerHash() ) );

	// Return the number of controllers updated - always 1 for animations...
	return(1);
}

/// End of function 'Asura_AnimationController_Animation::TweenBoneData'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::HandleMessage
// Author       : Andy Tate
// Date         : 21-Sep-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationController_Animation::HandleMessage (const u_int uType,
														 void* pvUserData)
{
	// Is this message intended for us?
	switch(uType)
	{
		case PLAY_MSG:
		{
			if(!IsPlaying())
			{
				Play();
			}
			return(true);
		}

		case PAUSE_MSG:
		{
			if(IsPlaying())
			{
				Pause();
			}
			return(true);
		}

		case STOP_MSG:
		{
			if(!IsFinished())
			{
				Stop();
			}
			return(true);
		}

		case DESTROY_MSG:
		{
			if((m_uControllerFlags & (AAC_FLAG_DESTROY_WHEN_FINISHED | AAC_FLAG_ALLOW_DESTROY_FROM_MESSAGE)) == 0)
			{
				return(false);
			}
			else
			{
				//Set the destroy when finished flag to ensure that this gets destroyed in the next update.
				//RAR 29/11/2006
				m_uControllerFlags |= AAC_FLAG_DESTROY_WHEN_FINISHED;
				if(!IsFinished())
				{
					Stop();
				}
				return(true);
			}
		}

		case DESTROY_FROM_TWEEN_IN_MSG :
		{
			if((m_uControllerFlags & (AAC_FLAG_DESTROY_WHEN_FINISHED | AAC_FLAG_ALLOW_DESTROY_FROM_MESSAGE)) == 0)
			{
				return(false);
			}
			else
			{
				//Set the destroy when finished flag to ensure that this gets destroyed in the next update.
				//RAR 29/11/2006
				m_uControllerFlags |= AAC_FLAG_DESTROY_WHEN_FINISHED;
				if(!IsFinished())
				{
					Stop();
				}

				//recurse back through previous animamations
				if(m_pxBlendToPreviousController && m_pxBlendToPreviousController->GetPreviousController())
				{
					m_pxBlendToPreviousController->GetPreviousController()->HandleMessage(DESTROY_FROM_TWEEN_IN_MSG);
				}
				return true;
			}

		}

		case SYNC_TIMER_TO_MSG:
		{
			Asura_AnimationMessage_SyncTimerTo*		pxSyncTimerTo	= reinterpret_cast<Asura_AnimationMessage_SyncTimerTo*>(pvUserData);
			Asura_AnimationController_Animation*	pxToAnimation	= m_pxAsuraAnimation->GetControllerAnimationFromHash(pxSyncTimerTo->m_uToAnimationHash);

			if(pxToAnimation != NULL)
			{
				SetParametricTimer(pxToAnimation->GetParametricTimer());
				// If we're syncing, we need to pause our timer so we don't move away from what we're told to use
				HandleMessage(PAUSE_MSG);
			}
			else if (IsPaused())
			{
				// We previously paused, which implies we were able to sync for a bit. We probably want to start playing ourselves again.
				// TODO - is this implication always valid?
				HandleMessage(PLAY_MSG);
			}
			return(true);
		}

		default:
		{
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_AnimationController_Animation::HandleMessage'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::UpdateTimers
// Author       : Andy Tate
// Date         : 15-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::UpdateTimers (const float fTimeStep, bool bIgnoreTimerFrameStamp)
{
	// Do we have a new state to change to?
	switch(m_eNewState)
	{
		case INVALID:
		{
			// Nothing to do here - carry on with whatever we were doing...
			break;
		}

		case PAUSED:
		case FINISHED:
		{
			m_eCurrentState	= m_eNewState;
			m_eNewState		= INVALID;
			break;
		}

		case PLAYING:
		{
			m_eCurrentState	= m_eNewState;
			m_eNewState		= INVALID;
			break;
		}

		default:
		{
			Asura_Assert(false, "Unexpected state to use when updating animation instance.");
			m_eNewState		= INVALID;
			return;
		}
	}		// End of switch.

	// Are we actually playing the animation?
	if(m_eCurrentState == PLAYING)
	{
		float fModifiedTimeStep = fTimeStep;

		// Yes - but are we still waiting to be played?
		if(m_fTimeDelay > 0.0f)
		{
			if(m_fTimeDelay < fModifiedTimeStep)
			{
				//Timestep is longer than the delay , so use up the delay , and see what time is left over
				fModifiedTimeStep -= m_fTimeDelay;
				m_fTimeDelay = 0.0f;
			}
			else
			{
				//time step is shorter than the delay , so just reduce the delay.
				fModifiedTimeStep = 0.0f;
				m_fTimeDelay	-= fTimeStep;
			}
		}
		
		if(m_fTimeDelay <= 0.0f)
		{
			const float	fParametricTimeStep	= fModifiedTimeStep * m_fOneOverDuration;
			
			const float fInitialParametricTargetTime = m_fParametricTargetTime;
			// No - the timers (checking for completion/looping)...
			m_fParametricTimer	+= fParametricTimeStep;
			if(m_fParametricTimer >= m_fParametricEndTime)
			{
				// We've finished - do we stop or restart?
				if((m_uControllerFlags & AACA_FLAG_LOOPED) || m_bUseParametricTargetTime)
				{
					float fParametricLength = m_fParametricEndTime - m_fParametricStartTime;
					if(fParametricLength)
					{
						//subtract enough loops of the animation to get the timer in the correct range again. RAR 24/11/2006
						float fLoops = (float)Asura_Maths::FloatToInt_Chop((m_fParametricTimer-m_fParametricStartTime)/fParametricLength);

						m_fParametricTimer	-= fLoops*fParametricLength;
						m_fParametricTimer = Asura_Maths::ClampToRange(m_fParametricTimer,m_fParametricStartTime,m_fParametricEndTime);

						if(m_bUseParametricTargetTime)
						{
						
							//subtract any loops from the target time as well
							m_fParametricTargetTime -= fLoops;
						}
					}
					else
					{
						if(m_bUseParametricTargetTime)
						{
							m_fParametricTimer = m_fParametricTargetTime;
						}
						else
						{
							m_fParametricTimer =  m_fParametricStartTime;
						}
					}
				}
				else
				{
					ChangeState(FINISHED);
					m_fParametricTimer = m_fParametricEndTime;
				}
			}

			if(m_bUseParametricTargetTime && m_fParametricTimer >= m_fParametricTargetTime)
			{
				//gone beyond requested point , so put the timer back and flag as finished.
				//The target time may have gone negative if too many loops were done this frame , so
				//bring back between 0 and 1 to be sure.
				//RAR 15/08/2007
				m_fParametricTimer = Asura_Maths::FloatMod(m_fParametricTargetTime,1.0f);
				if(m_fParametricTimer < 0.0f) m_fParametricTimer += 1.0f;
				if(fInitialParametricTargetTime > 0.0f && m_fParametricTimer == 0.0f)
				{
					//The target time was initially greater than 0 , but the timer has been wrapped back to 0.
					//Set the timer to the end time instead , which should mean that if the target time was 1 , then
					//it will end at the end of the loop instead of the start.
					//RAR 25/01/2008
					m_fParametricTimer = m_fParametricEndTime;
				}
				m_fParametricTargetTime = m_fParametricTimer;
				ChangeState(FINISHED);

			}
		}
	}
	// Ensure that the amount of animation played is kept up-to-date...
	// RAR 01/12/2006 Moved so that this gets done even if not playing , so that m_fOldAnimPlayed gets
	// updated appropriately when the animation finishes.
	UpdateAnimPlayed();

	m_bReadyForDestruction = m_bWillBeReadyForDestruction;
	m_bWillBeReadyForDestruction = (((m_uControllerFlags & AAC_FLAG_DESTROY_WHEN_FINISHED) != 0) && IsFinished());
}

/// End of function 'Asura_AnimationController_Animation::UpdateTimers'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetTimeDelay
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetTimeDelay (const float fTimeDelay)
{
	// +ve is a delay, -ve means play the animation from that point...
	if(fTimeDelay < 0.0f)
	{
		const float	fParametricTimeDelay	= -fTimeDelay * m_fOneOverDuration;

		if(m_bUseParametricTargetTime)
		{
			const float	fParametricLength	= m_fParametricEndTime - m_fParametricStartTime;
			if(fParametricLength)
			{
				m_fParametricTimer += fParametricTimeDelay;
				const float fInitialParametricTargetTime = m_fParametricTargetTime;
				if(m_fParametricTimer >= m_fParametricEndTime)
				{
				 	float fLoops = (float)Asura_Maths::FloatToInt_Chop((m_fParametricTimer-m_fParametricStartTime)/fParametricLength);
					m_fParametricTimer	-= fLoops*fParametricLength;
					m_fParametricTimer = Asura_Maths::ClampToRange(m_fParametricTimer,m_fParametricStartTime,m_fParametricEndTime);
					
					m_fParametricTargetTime -= fLoops;
				}
				if(m_fParametricTimer >= m_fParametricTargetTime)
				{
					//Gone beyond requested point , so put the timer back.
					//The target time may have gone negative if too many loops were done  , so
					//bring back between 0 and 1 to be sure.
					//RAR 15/08/2007
					m_fParametricTimer = Asura_Maths::FloatMod(m_fParametricTargetTime,1.0f);
					if(m_fParametricTimer<0.0f) m_fParametricTimer += 1.0f;
					if(fInitialParametricTargetTime > 0.0f && m_fParametricTimer == 0.0f)
					{
						//The target time was initially greater than 0 , but the timer has been wrapped back to 0.
						//Set the timer to the end time instead , which should mean that if the target time was 1 , then
						//it will end at the end of the loop instead of the start.
						//RAR 25/01/2008
						m_fParametricTimer = m_fParametricEndTime;
					}
					m_fParametricTargetTime = m_fParametricTimer;
				}
			}
		}
		else if(IsLooped())
		{
			const float	fWindowModulus	= m_fParametricEndTime - m_fParametricStartTime;

			// Looping - set the position within the animation...
			m_fParametricTimer	= m_fParametricStartTime + Asura_Maths::FloatMod(fParametricTimeDelay, fWindowModulus);
		}
		else
		{
			// Non-looping - clamp between start and end times...
			if(fParametricTimeDelay < m_fParametricStartTime)
			{
				m_fParametricTimer	= m_fParametricStartTime;
			}
			else if(fParametricTimeDelay > m_fParametricEndTime)
			{
				m_fParametricTimer	= m_fParametricEndTime;
			}
			else
			{
				m_fParametricTimer	= fParametricTimeDelay;
			}
		}

		m_fTimeDelay	= 0.0f;
		UpdateAnimPlayed();
	}
	else
	{
		// Set the delay on starting the animation...
		m_fTimeDelay	= fTimeDelay;
	}
}

/// End of function 'Asura_AnimationController_Animation::SetTimeDelay'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetDuration
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetDuration (const float fDuration)
{
	// Is the duration valid?
	if(fDuration <= 0.0f)
	{
		Asura_Assert(false, "Invalid duration set for animation.");
	}
	else
	{
		// Yes - set the duration...
		m_fDuration			= fDuration;
		m_fOneOverDuration	= 1.0f / fDuration;
	}
}

/// End of function 'Asura_AnimationController_Animation::SetDuration'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetDurationFromSpeed
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetDurationFromSpeed (const Asura_Hierarchy_Skin* pxSkin,const float fSpeed)
{
	if(m_pxAnimation == NULL)
	{
		Asura_Assert(false, "Invalid m_pxAnimation pointer in Asura_AnimationController_Animation");
		return;
	}

	// Is the speed valid?
	if(fSpeed == 0.0f)
	{
		Asura_Assert(false, "Invalid speed set for animation.");
	}
	else if(m_pxAnimation->Speed == 0.0f)
	{
		// No speed in the animation - this isn't good, but we can't really report it either...
	}
	else
	{
		const float	fDuration	= (m_pxAnimation->Duration * m_pxAnimation->Speed * m_pxAnimation->GetMovementScale(pxSkin)) / fSpeed;

		// Set the duration accordingly...
		SetDuration(fDuration);
	}
}

/// End of function 'Asura_AnimationController_Animation::SetDurationFromSpeed'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetParametricStartTime
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetParametricStartTime (const float fTime)
{
	// Is the new time valid?
	if(fTime > m_fParametricEndTime)
	{
		Asura_Assert(false, "Start time is set beyond the end time.");
	}
	else
	{
		// Set the start time...
		m_fParametricStartTime	= fTime;
	}
}

/// End of function 'Asura_AnimationController_Animation::SetParametricStartTime'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetParametricEndTime
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetParametricEndTime (const float fTime)
{
	// Is the new time valid?
	if(fTime < m_fParametricStartTime)
	{
		Asura_Assert(false, "Start time is set beyond the end time.");
	}
	else
	{
		// Set the start time...
		m_fParametricEndTime	= fTime;
	}
}

/// End of function 'Asura_AnimationController_Animation::SetParametricEndTime'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetParametricTimer
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetParametricTimer (const float fTime, bool bPlayToTime /* = true */)
{
	// Is the new time valid?
	if(fTime < 0.0f)
	{
		Asura_Assert(false, "Invalid time specfiied.");
	}
	else if(fTime > 1.0f)
	{
		Asura_Assert(false, "New time is beyond the end-time for this animation.");
	}
	else
	{
		// Update the clamped times - these are what we use for the updates...
		m_fParametricTimer	= fTime;
		UpdateAnimPlayed();
		if(!bPlayToTime)
		{
			m_fOldAnimPlayed = m_fAnimPlayed;
		}
	}
}

/// End of function 'Asura_AnimationController_Animation::SetParametricTimer'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetParametricTargetTime
// Author       : Richard Rice
// Date         : 15-Aug-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetParametricTargetTime (const float fTime)
{
	Asura_Assert(fTime >= m_fParametricTimer, "Target timer should be higher than the current timer");
	#ifdef ASURA_ASSERT
	float fFraction = Asura_Maths::FloatMod(fTime, 1.0f);
	Asura_Assert(fFraction >= m_fParametricStartTime && fFraction <= m_fParametricEndTime, "Fractional part of the target time should lie between the start and end times");
	#endif

	m_fParametricTargetTime = fTime;
	m_bUseParametricTargetTime = true;
}

/// End of function 'Asura_AnimationController_Animation::SetParametricEndTime'. ///


bool Asura_AnimationController_Animation::GetMovementOffset( Asura_Vector_3* pxOutputPos ) const
{
	if ( m_pxAsuraAnimation && m_pxAnimation )
	{
		return m_pxAnimation->GetMovementOffset( m_pxAsuraAnimation->GetSkin(), GetParametricTimer(), pxOutputPos, IsMirrored() );
	}
	else
	{
		return false;
	}
}

bool Asura_AnimationController_Animation::GetMovementOffset( Asura_Vector_3* pxOutputPos, Asura_Quat* pxOutputQuat ) const
{
	if ( m_pxAsuraAnimation && m_pxAnimation )
	{
		return m_pxAnimation->GetMovementOffset( m_pxAsuraAnimation->GetSkin(), GetParametricTimer(), pxOutputPos, pxOutputQuat, IsMirrored() );
	}
	else
	{
		return false;
	}
}


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::GetFlagsForLastFrame
// Author       : Andy Tate
// Date         : 19-Sep-2006
////////////////////////////////////////////////////////////////////////////////

int Asura_AnimationController_Animation::GetFlagsForLastFrame (void) const
{
	// Do we have an animation?
	if(m_pxAnimation == NULL)
	{
		// No - must be the rest-pose, this has no flags...
		return(0);
	}
	else
	{
		const Asura_Animation_Flag_Interval*	pxInterval	= m_pxAnimation->AnimationFlags;
		int										iResult		= 0;

		// Is the time-slice a single point in time?
		if(m_fOldAnimPlayed == m_fAnimPlayed)
		{
			// Yes - check each flag in-turn...
			for(int i = 0; i < m_pxAnimation->NumberOfAnimationFlags; i++, pxInterval++)
			{
				const float	fIntervalStart	= pxInterval->StartTime;
				const float	fIntervalEnd	= pxInterval->EndTime;
				const int	iIntervalFlag	= pxInterval->Flag;

				// Is it within our interval?
				// RAR 05/01/2007 - Flags that are at a single point in time don't get triggered when the timer isn't advancing
				if((m_fAnimPlayed >= fIntervalStart) && (m_fAnimPlayed <= fIntervalEnd) && fIntervalStart != fIntervalEnd)
				{
					iResult	|= iIntervalFlag;
				}
			}
		}
		else if(IsReversed())
		{
			// No - and we're playing backwards, check each flag in-turn...
			for(int i = 0; i < m_pxAnimation->NumberOfAnimationFlags; i++, pxInterval++)
			{
				const float	fIntervalStart	= pxInterval->StartTime;
				const float	fIntervalEnd	= pxInterval->EndTime;
				const int	iIntervalFlag	= pxInterval->Flag;

				//Flags that cover a single point in time only get triggered if m_fOldAnimPlayed <= FlagTime < m_fAnimPlayed
				//Unless the animation also stops on this frame , in which case it can also be triggered if FlagTime == m_fAnimPlayed
				//RAR 01/05/2007
				if((fIntervalStart == fIntervalEnd) && (fIntervalStart == m_fAnimPlayed) && !IsFinishing())
				{
					//The current timer is at the flag trigger time , but the animation isn't finising this frame.
					//So don't trigger the flag until the timer next advances , to avoid it triggering twice in a row.
					//RAR 01/05/2007
					continue;
				}

				// Have we looped during the last frame?
				if(m_fAnimPlayed > m_fOldAnimPlayed)
				{
					// Yes - because we have gone back in time...
					if(IntervalIntersects(fIntervalStart, fIntervalEnd, m_fOldAnimPlayed, m_fParametricStartTime) ||
						IntervalIntersects(fIntervalStart, fIntervalEnd, m_fParametricEndTime, m_fAnimPlayed))
					{
						iResult	|= iIntervalFlag;
					}
				}
				else
				{
					// No - so we can do a straightforward test...
					if(IntervalIntersects(fIntervalStart, fIntervalEnd, m_fAnimPlayed, m_fOldAnimPlayed))
					{
						iResult	|= iIntervalFlag;
					}
				}
			}
		}
		else
		{
			// No - and we are playing forwards, check each flag in-turn...
			for(int i = 0; i < m_pxAnimation->NumberOfAnimationFlags; i++, pxInterval++)
			{
				const float	fIntervalStart	= pxInterval->StartTime;
				const float	fIntervalEnd	= pxInterval->EndTime;
				const int	iIntervalFlag	= pxInterval->Flag;

				//Flags that cover a single point in time only get triggered if m_fOldAnimPlayed <= FlagTime < m_fAnimPlayed
				//Unless the animation also stops on this frame , in which case it can also be triggered if FlagTime == m_fAnimPlayed
				//RAR 01/05/2007
				if((fIntervalStart == fIntervalEnd) && (fIntervalStart == m_fAnimPlayed) && !IsFinishing())
				{
					//The current timer is at the flag trigger time , but the animation isn't finising this frame.
					//So don't trigger the flag until the timer next advances , to avoid it triggering twice in a row.
					//RAR 01/05/2007
					continue;
				}

				// Have we looped during the last frame?
				if(m_fAnimPlayed < m_fOldAnimPlayed)
				{
					// Yes - because we have gone back in time...
					if(IntervalIntersects(fIntervalStart, fIntervalEnd, m_fOldAnimPlayed, m_fParametricEndTime) ||
						IntervalIntersects(fIntervalStart, fIntervalEnd, m_fParametricStartTime, m_fAnimPlayed))
					{
						iResult	|= iIntervalFlag;
					}
				}
				else
				{
					// No - so we can do a straightforward test...
					if(IntervalIntersects(fIntervalStart, fIntervalEnd, m_fOldAnimPlayed, m_fAnimPlayed))
					{
						iResult	|= iIntervalFlag;
					}
				}
			}
		}

		// Return the result...
		return(iResult);
	}
}

/// End of function 'Asura_AnimationController_Animation::GetFlagsForLastFrame'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::PlaySoundsForLastFrame
// Author       : Andy Tate
// Date         : 21-Sep-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::PlaySoundsForLastFrame (void) const
{
	// Do we have an animation?
	if(m_pxAnimation != NULL)
	{
		// Get the centre of the sound from centre of the bounding sphere. This is because, that when we were taking centre of sound
		// from first bone, sometimes the bone was under the ground and sound was muted. So in theory, this could solve this.
		const Asura_Vector_3& xPosition = m_pxAsuraAnimation->GetBoundingSphereCentre();

		
		const Asura_Animation_Sound*	pxSound		= m_pxAnimation->m_pxAnimationSounds;

		//Don't won't to play instance  sounds if the timer isn't advancing.
		//This prevents sounds at time 0 being repeatedly played while doing a non-animated tween in.
		//Also presents sounds being repeated in hierarchy mode.
		//RAR 01/12/2006
		const bool bPlayInstantSounds = (m_fOldAnimPlayed != m_fAnimPlayed);
		//Only play looping sounds for animations that are playing , to prevent sounds from inactive
		//controllers from being used
		//RAR 05/12/2007
		const bool bPlayLoopingSounds = IsPlaying();

	
		if(IsReversed())
		{
			// We're playing backwards, check each sound in-turn...
			for(int i = 0; i < m_pxAnimation->m_iNumberOfAnimationSounds; i++, pxSound++)
			{
				const int	iSoundID	= pxSound->m_iAnimSoundID;

				//Is this a looping sound?
				if(pxSound->m_fStartTime != pxSound->m_fEndTime)
				{
					if(bPlayLoopingSounds)
					{
						//play the looping sound if in the correct interval
						if(pxSound->m_fStartTime <= m_fAnimPlayed && m_fAnimPlayed<= pxSound->m_fEndTime)
						{
							m_pxAsuraAnimation->PlayLoopedSound(iSoundID, *m_pxAnimation);
						}
					}
				}
				else
				{
					if(bPlayInstantSounds)
					{
						const float	fTime		= pxSound->m_fStartTime;
					
					
						// Have we looped during the last frame?
						if(m_fAnimPlayed > m_fOldAnimPlayed)
						{
							// Yes - because we have gone back in time...
							if( ((fTime >= m_fParametricStartTime) && (fTime <= m_fOldAnimPlayed)) ||
								((fTime > m_fAnimPlayed) && (fTime <= m_fParametricEndTime)) )
							{
								m_pxAsuraAnimation->PlaySound(iSoundID, *m_pxAnimation, xPosition, pxSound->m_uFlags);
							}
						}
						else
						{
							// No - so we can do a straightforward test...
							// RAR 01/12/2006 - Only play sounds that are exactly at the current timer
							// if the animation finished this frame , to prevent the chance of a sound
							// being played twice in a row.
							if( ((fTime > m_fAnimPlayed) && (fTime <= m_fOldAnimPlayed)) ||
								((fTime == m_fAnimPlayed) && (fTime == m_fParametricEndTime) && !IsLooped()) ) 
							{
								m_pxAsuraAnimation->PlaySound(iSoundID, *m_pxAnimation, xPosition, pxSound->m_uFlags);
							}
						}
					}
				}
			}
		}
		else
		{
			// We are playing forwards, check each sound in-turn...
			for(int i = 0; i < m_pxAnimation->m_iNumberOfAnimationSounds; i++, pxSound++)
			{
				const int	iSoundID	= pxSound->m_iAnimSoundID;

				//Is this a looping sound?
				if(pxSound->m_fStartTime != pxSound->m_fEndTime)
				{
					if(bPlayLoopingSounds)
					{
						//play the looping sound if in the correct interval
						if(pxSound->m_fStartTime <= m_fAnimPlayed && m_fAnimPlayed<= pxSound->m_fEndTime)
						{
							m_pxAsuraAnimation->PlayLoopedSound(iSoundID, *m_pxAnimation);
						}
					}
				}
				else
				{
					if(bPlayInstantSounds)
					{
						const float	fTime		= pxSound->m_fStartTime;
						
						// Have we looped during the last frame?
						if(m_fAnimPlayed < m_fOldAnimPlayed)
						{
							// Yes - because we have gone back in time...
							if( ((fTime >= m_fOldAnimPlayed) && (fTime <= m_fParametricEndTime)) ||
								((fTime >= m_fParametricStartTime) && (fTime < m_fAnimPlayed)) )
							{
								m_pxAsuraAnimation->PlaySound(iSoundID, *m_pxAnimation, xPosition, pxSound->m_uFlags);
							}
						}
						else
						{
							// No - so we can do a straightforward test...
							// RAR 01/12/2006 - Only play sounds that are exactly at the current timer
							// if the animation finished this frame , to prevent the chance of a sound
							// being played twice in a row.
							if( ((fTime >= m_fOldAnimPlayed) && (fTime < m_fAnimPlayed)) ||
								((fTime == m_fAnimPlayed) && (fTime == m_fParametricEndTime) && !IsLooped()) )

							{
								m_pxAsuraAnimation->PlaySound(iSoundID, *m_pxAnimation, xPosition, pxSound->m_uFlags);
							}
						}
					}
				}
			}
		}
	}
}

/// End of function 'Asura_AnimationController_Animation::PlaySoundsForLastFrame'. ///

#if (defined ASURA_USE_DEBUG_TEXT || defined ASURA_LOGFILES_ENABLED)
////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::PrintDebugInfo
// Author       : Kim Randell
// Date         : 07/02/2007
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::PrintDebugInfo(u_int& uRow, 
														 u_int& uCol,
														 const Asura_AnimationController_Group* pxParentGroup,
														 const char* szFilename /*= NULL*/) const
{
	const char*	szAnimFilename	= m_pxAnimation ? m_pxAnimation->GetName() : "*No Animation*";

	if(m_szName == NULL)
	{
		if ( szFilename == NULL )
		{
			Asura_DebugTextPrint(( uCol, uRow++, 0xffffffff, 0, "Pri = %d, Flags = %08X, Delta = %s, Anim = 0x%08x, Current State = %s, Next State = %s", m_uPriority, m_uControllerFlags, (m_bIsDeltaAnimation) ? "true" : "false", m_uControllerHash, s_aszStateString[ m_eCurrentState ], s_aszStateString[ m_eNewState ] ));
			Asura_DebugTextPrint(( uCol + 2, uRow, 0x80ffffff, 0, "Animation 0x%08x \"%s\": PTime %1.2f", m_uAnimationHash, szAnimFilename, Asura_Maths::FtoD(GetParametricTimer())));
		}
		else
		{
			for ( u_int uItr = 0; uItr < uCol; ++uItr )
			{
				Asura_SimpleLogfileOutput(( szFilename, " " ));
			}
			Asura_SimpleLogfileOutput(( szFilename, "Pri = %d, Flags = %08X, Delta = %s, Anim = 0x%08x, Current State = %s, Next State = %s\n", m_uPriority, m_uControllerFlags, (m_bIsDeltaAnimation) ? "true" : "false", m_uControllerHash, s_aszStateString[ m_eCurrentState ], s_aszStateString[ m_eNewState ] ));
			for ( u_int uItr = 0; uItr < uCol + 2; ++uItr )
			{
				Asura_SimpleLogfileOutput(( szFilename, " " ));
			}
			Asura_SimpleLogfileOutput(( szFilename, "Animation 0x%08x \"%s\": PTime %1.2f", m_uAnimationHash, szAnimFilename, Asura_Maths::FtoD(GetParametricTimer())));
		}
	}
	else
	{
		if ( szFilename == NULL )
		{
			Asura_DebugTextPrint(( uCol, uRow++, 0xffffffff, 0, "Pri = %d, Flags = %08X, Delta = %s, Anim = 0x%08x \"%s\", Current State = %s, Next State = %s", m_uPriority, m_uControllerFlags, (m_bIsDeltaAnimation) ? "true" : "false" , m_uControllerHash, m_szName, s_aszStateString[ m_eCurrentState ], s_aszStateString[ m_eNewState ] ));
			Asura_DebugTextPrint(( uCol + 2, uRow, 0x80ffffff, 0, "Animation 0x%08x \"%s\": PTime %1.2f", m_uAnimationHash, szAnimFilename, Asura_Maths::FtoD(GetParametricTimer()) ));
		}
		else
		{
			for ( u_int uItr = 0; uItr < uCol; ++uItr )
			{
				Asura_SimpleLogfileOutput(( szFilename, " " ));
			}
			Asura_SimpleLogfileOutput(( szFilename, "Pri = %d, Flags = %08X, Delta = %s, Anim = 0x%08x: \"%s\", Current State = %s, Next State = %s\n", m_uPriority, m_uControllerFlags, (m_bIsDeltaAnimation) ? "true" : "false", m_uControllerHash, m_szName, s_aszStateString[ m_eCurrentState ], s_aszStateString[ m_eNewState ] ));
			for ( u_int uItr = 0; uItr < uCol + 2; ++uItr )
			{
				Asura_SimpleLogfileOutput(( szFilename, " " ));
			}
			Asura_SimpleLogfileOutput(( szFilename, "Animation 0x%08x \"%s\": PTime %1.2f", m_uAnimationHash, szAnimFilename, Asura_Maths::FtoD(GetParametricTimer())));
		}
	}

	if(m_pxAnimation != NULL)
	{
		const u_int	uNumBones	= m_pxAnimation->GetNumberOfBones();
		u_int		uX			= uCol + 37 + strlen( szAnimFilename );

		char		szTemp[10];

		if ( szFilename == NULL )
		{
			Asura_DebugTextPrint(( uX, uRow, 0x80ffffff, 0, ", Bones:"));
		}
		else
		{
			Asura_SimpleLogfileOutput(( szFilename, ", Bones: " ));
		}
		uX	+= 9;

		for(u_int u = 0; u < uNumBones; u++)
		{
			if(m_pxAnimation->BoneAnims && m_pxAnimation->BoneAnims[u].NumberOfKeyFrames > 0)
			{
				const u_int	uNext	= u + 1;

				// Find out whether there are more bones continuously used...
				for(u_int v = uNext; (u < v) && (v <= uNumBones); v++)
				{
					if((v == uNumBones) || (m_pxAnimation->BoneAnims[v].NumberOfKeyFrames <= 0))
					{
						if(v == uNext)
						{
							Asura_PrintfToBfr(szTemp, "%d", 10, true, u);
						}
						else
						{
							Asura_PrintfToBfr(szTemp, "%d-%d", 10, true, u, (v - 1));
						}

						// Time to break out of this for-loop...
						u	= v;
						v	= uNumBones + 1;
					}
				}

				// Output this line of text...
				if ( szFilename == NULL )
				{
					Asura_DebugTextPrint((uX, uRow, 0x80ffffff, 0, szTemp));
				}
				else
				{
					Asura_SimpleLogfileOutput(( szFilename, "%s ", szTemp ));
				}

				uX	+= (strlen(szTemp) + 1);
			}
		}
	}

	if ( szFilename == NULL )
	{
		uRow++;
	}
	else
	{
		Asura_SimpleLogfileOutput(( szFilename, "\n" ));
	}
}

/// End of function 'Asura_AnimationController_Animation::ReplaceBlendToPreviousController'. ///
#endif // #if (defined ASURA_USE_DEBUG_TEXT || defined ASURA_LOGFILES_ENABLED)


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::GetAdjustedAnimPlayed
// Author       : Kim Randell
// Date         : 12/02/2007
////////////////////////////////////////////////////////////////////////////////
float Asura_AnimationController_Animation::GetAdjustedAnimPlayed() const
{
	if((m_uControllerFlags & AACA_FLAG_LOOPED))
	{
		const float fAnimPlayed = Asura_Maths::FloatMod(m_fAnimPlayed, 1.0f);
		return (fAnimPlayed < 0.0f) ? fAnimPlayed + 1.0f : fAnimPlayed;
	}
	else
	{
		return Asura_Maths::ClampToRange(m_fAnimPlayed, 0.f, 1.0f);
	}
}
/// End of function 'Asura_AnimationController_Animation::PlaySoundsForLastFrame'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::Duplicate
// Author       : Kim Randell
// Date         : 03/05/2007
////////////////////////////////////////////////////////////////////////////////

Asura_AnimationController* Asura_AnimationController_Animation::Duplicate(Asura_Animation* pxDestAnimation) const
{
	// Get the parent group
	Asura_AnimationController_Group* pxParentGroup = pxDestAnimation->GetControllerGroupFromHash(m_uParentControllerHash);

	// Create a new blend
	Asura_AnimationBlend* pxBlend = m_pxBlendToPreviousController ? m_pxBlendToPreviousController->Duplicate() : NULL;

	Asura_AnimationController_Animation* pxNewAnimController =
		pxDestAnimation->CreateControllerAnimation( m_uControllerHash, m_uControllerFlags, m_uPriority, m_uAnimationHash, pxBlend, pxParentGroup ); 

	// Copy members
	pxNewAnimController->m_fDuration = m_fDuration;
	pxNewAnimController->m_fOneOverDuration = m_fOneOverDuration;
	pxNewAnimController->m_fTimeDelay = m_fTimeDelay;
	pxNewAnimController->m_fParametricStartTime = m_fParametricStartTime;
	pxNewAnimController->m_fParametricEndTime = m_fParametricEndTime;
	pxNewAnimController->m_fParametricTimer = m_fParametricTimer;
	pxNewAnimController->m_fParametricTargetTime = m_fParametricTargetTime;
	pxNewAnimController->m_fAnimPlayed = m_fAnimPlayed;
	pxNewAnimController->m_fOldAnimPlayed = m_fOldAnimPlayed;
	pxNewAnimController->m_fTimeUntilFinished = m_fTimeUntilFinished;
	pxNewAnimController->m_uControllerAnimationFlags = m_uControllerAnimationFlags;

	return(pxNewAnimController);
}

/// End of function 'Asura_AnimationController_Animation::Duplicate'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationController_Animation::SetEmptyAnimationAsDelta
// Author       : Andy Tate
// Date         : 8-Sep-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationController_Animation::SetEmptyAnimationAsDelta(void)
{
	if(m_pxAnimation == NULL)
	{
		m_bIsDeltaAnimation	= true;
	}
	else
	{
		Asura_Assert(false, "Animation cannot be set to a delta because its not empty.");
	}
}

/// End of function 'Asura_AnimationController_Animation::SetEmptyAnimationAsDelta'. ///


/// End of file. ///
