/**
*
* \file Asura_Animation_Posable.cpp
*
* Created by Richard May - 30/08/2002 16:21:13
*
* Copyright (C) 2002 Rebellion, All Rights Reserved.
*
* \brief
*
**/



/************/
/* Includes.*/
/************/

#include "Core/Asura.h"

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

#include "Animation/Asura_Hierarchy.h"

/************/
/* Classes. */
/************/

/* Constructors */

Asura_Animation_Posable::Asura_Animation_Posable( Asura_Hash_ID uSkinNameHash )
    : Asura_Animation( uSkinNameHash )														   
{
    m_bPosable = true;
    ResetAllOrientations();
}

Asura_Animation_Posable::Asura_Animation_Posable( const char* szSkinName )
    : Asura_Animation( Asura_GetHashID(szSkinName) )
{
    m_bPosable = true;
    ResetAllOrientations();
}

/* Destructor */
Asura_Animation_Posable::~Asura_Animation_Posable()														   
{
}

void Asura_Animation_Posable::ResetAllOrientations( void )
{
	m_xHipOrient.Zero();
	m_xHeadOrient.Zero();
}

/*************************************************************
 *
 * Name          : UpdatePosableBones
 *
 * Author        : Richard May
 *
 * Description   : Updates the position and orientation of
 *				   the animations bones, including the posable
 *				   data for yaw & pitch of the head/chest and 
 *				   right arm.
 *				   This is a default implementation that should
 *				   be overridden by projects with different requirements.
 *
 * Parameters    : none
 *
 * Return value  : void
 *
 *************************************************************/
void Asura_Animation_Posable::UpdatePosableBones()
{
	const int* piPosableBoneIndexArray = GetPosableBoneIndexArray();
	if( !piPosableBoneIndexArray ) return;

	const float fHipOrientX = GetHipOrient().x;
	const float fHipOrientY = GetHipOrient().y;
	const float fHeadOrientX = GetHeadOrient().x;
	const float fHeadOrientY = GetHeadOrient().y;

	// Special handling for SPINE_02 which is optional.
	const int iSpine2Idx = piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_SPINE_02];
	const int iEnd = ( -1 == iSpine2Idx ? ASURA_ANIMATION_POSABLE_BONE_SPINE_01 : ASURA_ANIMATION_POSABLE_BONE_SPINE_02 );

	Asura_Hierarchy_Tween* pxTween = GetTweenStore();

	Asura_Quat xNeckQuat;
	Asura_Quat xSpine1Quat;
	Asura_Quat xTurretQuat;
	Asura_Quat xPelvisQuat;
	Asura_Quat xBip01Quat;

	// (MH - 07.08.2003) store ptr to quat at each level once then use below
	pxTween->GetBoneData(piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_NECK]).GetOrientation(xNeckQuat);
	pxTween->GetBoneData(piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_NECK]).GetOrientation(xSpine1Quat);
	pxTween->GetBoneData(piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_NECK]).GetOrientation(xTurretQuat);
	pxTween->GetBoneData(piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_NECK]).GetOrientation(xPelvisQuat);
	pxTween->GetBoneData(piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_NECK]).GetOrientation(xBip01Quat);

	Asura_Quat	xSpine2Quat;

	if(-1 == iSpine2Idx)
	{
		xSpine2Quat	= Asura_Maths::IdentityQuat;
	}
	else
	{
		pxTween->GetBoneData(iSpine2Idx).GetOrientation(xSpine2Quat);
	}

	for( int iBone = ASURA_ANIMATION_POSABLE_BONE_TURRET; iBone <= iEnd; ++iBone )
	{
		const int								iBoneIndex	= piPosableBoneIndexArray[iBone];
		Asura_Hierarchy_TweenStore_BoneData&	xBoneData	= pxTween->GetBoneData(iBoneIndex);

		Asura_Quat								q;

		xBoneData.GetOrientation(q);

		/// Richard May - 17/09/2002 16:08:34 - repose the bones to the correct orientations
		if( iBone == ASURA_ANIMATION_POSABLE_BONE_TURRET )
		{
			Asura_Quat xTempQuat;

			xTempQuat = Asura_Quat_Invert(q);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xPelvisQuat);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xBip01Quat);

			// yaw
			Asura_Quat xHipYaw;
			Asura_Vector_3 xBoneAxis = Asura_Vector_3( 0.0f, 1.0f, 0.0f );
			xBoneAxis = xTempQuat.RotateVector(xBoneAxis);

			xHipYaw.FromRotation( (fHipOrientY * 0.1f), xBoneAxis );
			q = q * xHipYaw;
			
			// pitch
			Asura_Quat xHipPitch;
			xBoneAxis = Asura_Vector_3( 1.0f, 0.0f, 0.0f );
			xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
			xHipPitch.FromRotation( (fHipOrientX * 0.1f), xBoneAxis );
			q = q * xHipPitch;		
			
		}			
		else if(iBone == ASURA_ANIMATION_POSABLE_BONE_SPINE_01)
		{	
			Asura_Quat xTempQuat;

			xTempQuat = Asura_Quat_Invert(q);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xTurretQuat);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xPelvisQuat);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xBip01Quat);
			
			// yaw
			Asura_Quat xHipYaw;
 			Asura_Vector_3 xBoneAxis = Asura_Vector_3( 0.0f, 1.0f, 0.0f );
			xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
			xHipYaw.FromRotation( (fHipOrientY * 0.3f), xBoneAxis );
			q = q * xHipYaw;

			// pitch
			Asura_Quat xHipPitch;
			xBoneAxis = Asura_Vector_3( 1.0f, 0.0f, 0.0f );
			xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
			xHipPitch.FromRotation( (fHipOrientX * 0.3f), xBoneAxis );
			q = q * xHipPitch;				
		}
		else if(iBone == ASURA_ANIMATION_POSABLE_BONE_SPINE_02)
		{	
			Asura_Quat xTempQuat;

			xTempQuat = Asura_Quat_Invert(q);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xSpine1Quat);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xTurretQuat);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xPelvisQuat);
			xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xBip01Quat);

			// yaw
			Asura_Quat xHipYaw;
 			Asura_Vector_3 xBoneAxis = Asura_Vector_3( 0.0f, 1.0f, 0.0f );
			xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
			xHipYaw.FromRotation( (fHipOrientY * 0.6f), xBoneAxis );
			q = q * xHipYaw;				
			
			// pitch
			Asura_Quat xHipPitch;
			xBoneAxis = Asura_Vector_3( 1.0f, 0.0f, 0.0f );
			xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
			xHipPitch.FromRotation( (fHipOrientX * 0.6f), xBoneAxis );
			q = q * xHipPitch;	
		}

		xBoneData.SetOrientation(q);
	}
	
	int iBoneIndex = piPosableBoneIndexArray[ASURA_ANIMATION_POSABLE_BONE_HEAD];

	Asura_Quat	q;

	pxTween->GetBoneData(iBoneIndex).GetOrientation(q);

	Asura_Quat xTempQuat;

	xTempQuat = Asura_Quat_Invert(q);
	xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xNeckQuat);
	xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xSpine2Quat);
	xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xSpine1Quat);
	xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xTurretQuat);
	xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xPelvisQuat);
	xTempQuat = Asura_Quat_MultiplyByInverse(xTempQuat, xBip01Quat);

	// yaw
	Asura_Quat xHeadYaw;
	Asura_Vector_3 xBoneAxis = Asura_Vector_3( 0.0f, 1.0f, 0.0f );
	xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
	xHeadYaw.FromRotation( (fHeadOrientY), xBoneAxis );
	q = q * xHeadYaw;				
	
	// pitch
	Asura_Quat xHeadPitch;
	xBoneAxis = Asura_Vector_3( 1.0f, 0.0f, 0.0f );
	xBoneAxis = xTempQuat.RotateVector(xBoneAxis);
	xHeadPitch.FromRotation( (fHeadOrientX), xBoneAxis );
	q = q * xHeadPitch;

	pxTween->GetBoneData(iBoneIndex).SetOrientation(q);
} 
/* end of function UpdatePosableBones */

Asura_Quat Asura_Animation_Posable::Asura_Quat_MultiplyByInverse(const Asura_Quat& q1, const Asura_Quat& q2)
{
	Asura_Vector_3 v1(q1.x, q1.y, q1.z);
	Asura_Vector_3 v2(-q2.x, -q2.y, -q2.z);
	Asura_Vector_3 temp = (v1 * q2.w) + (v2 * q1.w) + (v1 ^ v2);

	return Asura_Quat(temp.x, temp.y, temp.z, (q1.w * q2.w) - (v1 * v2));
}

void Asura_Animation_Posable::Copy( const Asura_Animation* pxSrcAnim, bool bCopySkin, bool bCopyOneShotAnims, bool bCopyAttachments )
{
	//copy base class
	Asura_Animation::Copy(pxSrcAnim,bCopySkin,bCopyOneShotAnims,bCopyAttachments);

	//If the source is also posable ,then copy the posable stuff
	if(pxSrcAnim->IsPosableAnim())
	{
		const Asura_Animation_Posable* pxSrcAnimPosable = (const Asura_Animation_Posable*) pxSrcAnim;

		m_xHipOrient = pxSrcAnimPosable->m_xHipOrient;
		m_xHeadOrient = pxSrcAnimPosable->m_xHeadOrient;
	}
}
