#include "Mix/Tool/Win32/Core/Graphics/Motion.h"

#include "Mix/Tool/Win32/Core/Directory.h"
#include "Mix/Tool/Win32/Core/Graphics/Node.h"
#include "Mix/Tool/Win32/Core/Graphics/MotionCurve.h"
#include "Mix/Tool/Win32/Core/Graphics/RootMotionCurve.h"
#include "Mix/Tool/Win32/Core/Graphics/MotionController.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

const wchar_t* Motion::DEF_NAME = L"Motion";
const wchar_t* Motion::FILE_EXT = L".mot";

Motion::Motion( Mix::Tool::Win32::Graphics::ObjectModel* pObjectModel, float framesPerSec ) :
m_pObjectModel( pObjectModel ),
m_pController( NULL ),
m_DefaultName( Motion::DEF_NAME ),
m_Name( Motion::DEF_NAME ),
m_bDefault( true ),
m_BlendRatio( 1.0f ),
m_FramesPerSec( framesPerSec ),
m_LastTime( 0.0f ),
m_LoopStartTime( 0.0f ),
m_LoopEndTime( 0.0f ),
m_bLoop( true ),
m_Speed( 1.0f ),
m_RTFlags( MOTION_RT_ROTATION | MOTION_RT_TRANSLATION ),
m_pRTCurve( NULL ),
m_SaveFilePath( L"" )
{
	const Mix::Tool::Win32::Graphics::Node* pRootNode = m_pObjectModel->GetRootNode();
	m_pRTCurve = new Mix::Tool::Win32::Graphics::RootMotionCurve();
}

Motion::~Motion( void )
{
	Release();
}

void Motion::SetController( Mix::Tool::Win32::Graphics::MotionController* pController )
{
	if( m_pController == pController )
	{
		return;
	}

	if( m_pController != NULL )
	{
		m_pController->Detach( this );
	}

	m_pController = pController;
	if( m_pController != NULL )
	{
		m_pController->Attach( this );
	}
}

void Motion::Release( void )
{
	MIX_DELETE( m_pRTCurve );

	if( m_CurveList.size() > 0 )
	{
		for( Motion::CurveList::iterator it = m_CurveList.begin(); it != m_CurveList.end(); ++it )
		{
			MIX_DELETE( ( *it ) );
		}

		m_CurveList.clear();
	}

	m_BlendRatio = 1.0f;
	m_FramesPerSec = 0.0f;
	m_LastTime = 0.0f;
	m_LoopStartTime = 0.0f;
	m_LoopEndTime = 0.0f;
	m_bLoop = true;
}

unsigned int Motion::GetCurveCount( void ) const
{
	return m_CurveList.size();
}

Mix::Tool::Win32::Graphics::MotionCurve* Motion::GetCurve( unsigned int index ) const
{
	if( m_CurveList.size() <= index )
	{
		return NULL;
	}

	return m_CurveList[index];
}

Mix::Tool::Win32::Graphics::MotionCurve* Motion::AddCurve( const char* pLinkNodeName )
{
	std::wstring temp;

	AnsiToWide( pLinkNodeName, temp );

	return AddCurve( temp.c_str() );
}

Mix::Tool::Win32::Graphics::MotionCurve* Motion::AddCurve( const wchar_t* pLinkNodeName )
{
	Mix::Tool::Win32::Graphics::MotionCurve* pCurve;

	pCurve = new Mix::Tool::Win32::Graphics::MotionCurve( static_cast<int>( m_CurveList.size() ), pLinkNodeName );
	if( pCurve == NULL )
	{
		return NULL;
	}

	m_CurveList.push_back( pCurve );

	return pCurve;
}

void Motion::SetDefaultName( const wchar_t* pName )
{
	MIX_ASSERT( pName != NULL );
	MIX_ASSERT( ::wcslen( pName ) > 0 );

	m_DefaultName = pName;
}

const wchar_t* Motion::GetDefaultName( void ) const
{
	return m_DefaultName.c_str();
}

void Motion::SetName( const wchar_t* pName )
{
	MIX_ASSERT( m_pController != NULL );

	m_pController->CreateMotionName( this, pName, m_Name );
}

const wchar_t* Motion::GetName( void ) const
{
	return m_Name.c_str();
}

bool Motion::IsAssigned( void ) const
{
	MIX_ASSERT( m_pController != NULL );

	return m_pController->IsAssigned();
}

void Motion::SetDefault( bool state )
{
	m_bDefault = state;
}

bool Motion::GetDefault( void ) const
{
	return m_bDefault;
}

void Motion::SetSpeed( float speed )
{
	m_Speed = max( 0.0f, speed );
}

float Motion::GetSpeed( void ) const
{
	return m_Speed;
}

void Motion::SetBlendRatio( float rate )
{
	m_BlendRatio = rate;

	if( m_BlendRatio < 0.0f )
	{
		m_BlendRatio = 0.0f;
	}
	else if( m_BlendRatio > 1.0f )
	{
		m_BlendRatio = 1.0f;
	}
}

float Motion::GetBlendRatio( void ) const
{
	return m_BlendRatio;
}

float Motion::GetLastTime( void ) const
{
	return m_LastTime;
}

void Motion::SetLoopStartTime( float time )
{
	if( m_LoopEndTime < time )
	{
		return;
	}

	if( 0.0f > time )
	{
		m_LoopStartTime = 0.0f;
	}
	else if( m_LastTime < time )
	{
		m_LoopStartTime = m_LastTime;
	}
	else
	{
		m_LoopStartTime = time;
	}
}

void Motion::SetLoopEndTime( float time )
{
	if( m_LoopStartTime > time )
	{
		return;
	}

	if( 0.0f > time )
	{
		m_LoopEndTime = 0.0f;
	}
	else if( m_LastTime < time )
	{
		m_LoopEndTime = m_LastTime;
	}
	else
	{
		m_LoopEndTime = time;
	}
}

float Motion::GetLoopStartTime( void ) const
{
	return m_LoopStartTime;
}

float Motion::GetLoopEndTime( void ) const
{
	return m_LoopEndTime;
}

unsigned int Motion::GetRootTransformFlags( void ) const
{
	return m_RTFlags;
}

void Motion::SetRootTransformFlags( unsigned int flags )
{
	m_RTFlags = MIX_TEST_BIT( flags, MOTION_RT_MASK );
}

void Motion::UpdateRootTransform( void )
{
	const wchar_t* pNodeName = m_pObjectModel->GetRootTransformNodeName();

	const MOTION_RT_AXIS rAxis = m_pObjectModel->GetRootRotationAxis();
	const MOTION_RT_AXIS tAxis = m_pObjectModel->GetRootTranslationAxis();

	Motion::CurveList::iterator it_begin = m_CurveList.begin();
	Motion::CurveList::iterator it_end = m_CurveList.end();
	Motion::CurveList::iterator it;

	Mix::Tool::Win32::Graphics::MotionCurve* pCurve = NULL;

	unsigned int rKeyCount = 0;
	unsigned int tKeyCount = 0;

	const Mix::Tool::Win32::Graphics::Node* pNode;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 対象とするカーブを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( pNodeName == NULL ) ||
		( m_CurveList.size() == 0 ) )
	{
		//カーブが存在しない
		m_pRTCurve->Destroy();
		return;
	}
	else
	{
		for( it = it_begin; ( it != it_end ) && ( pCurve == NULL ); ++it )
		{
			if( ::wcscmp( ( *it )->GetLinkNodeName(), pNodeName ) != 0 )
			{
				continue;
			}

			pCurve = ( *it );
		}

		if( pCurve == NULL )
		{
			return;
		}

		rKeyCount = pCurve->GetRotationKeyCount();
		tKeyCount = pCurve->GetTranslationKeyCount();

		m_pRTCurve->Initialize( rKeyCount, tKeyCount );
	}

#if 1

	D3DXMATRIX baseMat;
	D3DXMATRIX castMat;
	D3DXQUATERNION invDefWorldRot;
	D3DXVECTOR3 defWorldPos;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートトランスフォームに必要な定数を取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pNode = m_pObjectModel->GetRootTransformConstant( baseMat, castMat, invDefWorldRot, defWorldPos );
	if( pNode == NULL )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ローテーション
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_ROTATION ) == MOTION_RT_ROTATION ) &&
		( rKeyCount > 0 ) )
	{
		for( unsigned int i = 0; i < rKeyCount; i++ )
		{
			const Mix::Tool::Win32::Graphics::QUATERNION_KEY& key = pCurve->GetRotationKey( i );

			float time = key.time;

			D3DXVECTOR3 localS = pCurve->GetScaling( time );
			const D3DXQUATERNION& localR = key.value;
			D3DXVECTOR3 localT = pCurve->GetTranslation( time );

			D3DXMATRIX tempMat;
			D3DXQUATERNION tempQuat;

			D3DXMATRIX worldMat;

			D3DXMATRIX worldRootMat;
			D3DXQUATERNION worldRootRot;

			/*
				真下(XZ平面)にキャストしたワールド行列を求める
			*/

			D3DXMatrixScaling( &worldMat, localS.x, localS.y, localS.z );
			worldMat *= *D3DXMatrixRotationQuaternion( &tempMat, &localR );
			worldMat._41 = localT.x; worldMat._42 = localT.y; worldMat._43 = localT.z; worldMat._44 = 1.0f;

			worldMat = *D3DXMatrixMultiply( &tempMat, &worldMat, &baseMat );
			worldRootMat = *D3DXMatrixMultiply( &tempMat, &castMat, &worldMat );

			/*
				回転を取り出す
			*/

			Mix::Tool::Win32::Matrix_Decompose( worldRootMat, NULL, &worldRootRot, NULL );

			/*
				回転の差分を求める
			*/

			worldRootRot = *D3DXQuaternionMultiply( &tempQuat, &worldRootRot, &invDefWorldRot );
			worldRootRot = *D3DXQuaternionNormalize( &tempQuat, &worldRootRot );

			/*
				回転軸を変換
			*/

			worldRootRot = ConvertRootRotationAxis( worldRootRot, rAxis );

			/*
				キーを追加
			*/

			m_pRTCurve->AddRotationKey( time, worldRootRot );

			/*
				移動が無効な場合は、ルート( 0.0f, 0.0f, 0.0f )を回転した際の差分を適用する
			*/

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION ) == 0 )
			{
				D3DXMATRIX worldRootRotMat;

				D3DXVECTOR3 localRootPos0;
				D3DXVECTOR3 localRootPos1;

				D3DXVECTOR3 valueT;

				D3DXMatrixRotationQuaternion( &worldRootRotMat, &worldRootRot );

				localRootPos0.x = -worldMat._41;
				localRootPos0.y = -worldMat._42;
				localRootPos0.z = -worldMat._43;

				D3DXVec3TransformCoord( &localRootPos1, &localRootPos0, &worldRootRotMat );

				valueT.x = localRootPos1.x - localRootPos0.x;
				valueT.y = localRootPos1.y - localRootPos0.y;
				valueT.z = localRootPos1.z - localRootPos0.z;

				m_pRTCurve->AddTranslationKey( time, valueT );
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランスレーション
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION ) != 0 ) &&
		( tKeyCount > 0 ) )
	{
		for( unsigned int i = 0; i < tKeyCount; i++ )
		{
			const Mix::Tool::Win32::Graphics::VECTOR_KEY& key = pCurve->GetTranslationKey( i );

			float time = key.time;

			D3DXVECTOR3 localS = pCurve->GetScaling( time );
			D3DXQUATERNION localR = pCurve->GetRotation( time );
			const D3DXVECTOR3& localT = key.value;

			D3DXMATRIX tempMat;

			D3DXMATRIX worldMat;
			D3DXVECTOR3 worldPos;

			/*
				ローカル行列の作成
			*/

			D3DXMatrixScaling( &worldMat, localS.x, localS.y, localS.z );
			worldMat *= *D3DXMatrixRotationQuaternion( &tempMat, &localR );
			worldMat._41 = localT.x; worldMat._42 = localT.y; worldMat._43 = localT.z; worldMat._44 = 1.0f;

			/*
				ワールド行列に変換し、位置を取得
			*/

			worldMat = *D3DXMatrixMultiply( &tempMat, &worldMat, &baseMat );
			Mix::Tool::Win32::Matrix_Decompose( worldMat, NULL, NULL, &worldPos );

			/*
				初期座標からのオフセットに直す
			*/

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION_XZ ) == MOTION_RT_TRANSLATION_XZ )
			{
				worldPos.x -= defWorldPos.x;
				worldPos.z -= defWorldPos.z;
			}
			else
			{
				worldPos.x = 0.0f;
				worldPos.z = 0.0f;
			}

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION_Y ) == MOTION_RT_TRANSLATION_Y )
			{
				worldPos.y -= defWorldPos.y;
			}
			else
			{
				worldPos.y = 0.0f;
			}

			/*
				回転が有効な場合は、ルート( worldPos )を回転した際の差分を適用する
			*/

			if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_ROTATION ) == MOTION_RT_ROTATION ) &&
				( rKeyCount > 0 ) )
			{
				D3DXQUATERNION tempQuat;

				D3DXMATRIX worldRootMat;
				D3DXQUATERNION worldRootRotQuat;
				D3DXMATRIX worldRootRotMat;

				D3DXVECTOR3 localRootPos0;
				D3DXVECTOR3 localRootPos1;

				worldRootMat = *D3DXMatrixMultiply( &tempMat, &castMat, &worldMat );
				Mix::Tool::Win32::Matrix_Decompose( worldRootMat, NULL, &worldRootRotQuat, NULL );

				worldRootRotQuat = *D3DXQuaternionMultiply( &tempQuat, &worldRootRotQuat, &invDefWorldRot );
				worldRootRotQuat = *D3DXQuaternionNormalize( &tempQuat, &worldRootRotQuat );

				D3DXMatrixRotationQuaternion( &worldRootRotMat, &worldRootRotQuat );

				localRootPos0.x = worldPos.x - worldMat._41;
				localRootPos0.y = worldPos.y - worldMat._42;
				localRootPos0.z = worldPos.z - worldMat._43;

				D3DXVec3TransformCoord( &localRootPos1, &localRootPos0, &worldRootRotMat );

				worldPos.x += localRootPos1.x - localRootPos0.x;
				worldPos.y += localRootPos1.y - localRootPos0.y;
				worldPos.z += localRootPos1.z - localRootPos0.z;
			}

			/*
				移動軸を変換
			*/

			worldPos = ConvertRootTranslationAxis( worldPos, tAxis ); 

			/*
				キーを追加
			*/

			m_pRTCurve->AddTranslationKey( time, worldPos );
		}
	}

#endif

#if 0

	D3DXMATRIX baseMat;
	D3DXMATRIX castMat;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートトランスフォームに必要な定数を取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pNode = m_pObjectModel->GetRootTransformConstant( baseMat, castMat );
	if( pNode == NULL )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ローテーション & ディレクション
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_ROTATION ) == MOTION_RT_ROTATION ) &&
		( rKeyCount > 0 ) )
	{
		D3DXMATRIX tempMat;
		D3DXMATRIX localMat;
		D3DXMATRIX worldMat;

		D3DXQUATERNION tempRot;
		D3DXQUATERNION invLocalBaseRot;
		D3DXQUATERNION invWorldBaseRot;
		D3DXQUATERNION rot;

		D3DXQuaternionInverse( &invLocalBaseRot, &( pNode->GetDefLocalRotation() ) );

		worldMat = *D3DXMatrixMultiply( &tempMat, &( pNode->GetDefLocalTransform() ), &baseMat );
		worldMat = *D3DXMatrixMultiply( &tempMat, &castMat, &worldMat );
		Mix::Tool::Win32::Matrix_Decompose( worldMat, NULL, &invWorldBaseRot, NULL );
		invWorldBaseRot = *D3DXQuaternionInverse( &tempRot, &invWorldBaseRot );

		for( unsigned int i = 0; i < rKeyCount; i++ )
		{
			const Mix::Tool::Win32::Graphics::QUATERNION_KEY& key = pCurve->GetRotationKey( i );
			const D3DXQUATERNION& localR = key.value;
			float time = key.time;

			/*
				ローテーション
			*/

			rot = *D3DXQuaternionMultiply( &tempRot, &localR, &invLocalBaseRot );
			rot = *D3DXQuaternionNormalize( &tempRot, &rot );

			m_pRTCurve->AddRotationKey( time, rot );

			/*
				ディレクション
			*/

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_DIRECTION ) != 0 )
			{
				D3DXVECTOR3 localS = pCurve->GetScaling( time );
				D3DXVECTOR3 localT = pCurve->GetTranslation( time );

				D3DXVECTOR3 angles;

				//ローカル行列を作成
				D3DXMatrixScaling( &localMat, localS.x, localS.y, localS.z );
				localMat *= *D3DXMatrixRotationQuaternion( &tempMat, &localR );
				localMat._41 = localT.x; localMat._42 = localT.y; localMat._43 = localT.z; localMat._44 = 1.0f;
	
				//ワールド行列に変換
				worldMat = *D3DXMatrixMultiply( &tempMat, &localMat, &baseMat );
				worldMat = *D3DXMatrixMultiply( &tempMat, &castMat, &worldMat );

				//回転を取り出す
				Mix::Tool::Win32::Matrix_Decompose( worldMat, NULL, &rot, NULL );

				//回転の差分を求める
				rot = *D3DXQuaternionMultiply( &tempRot, &rot, &invWorldBaseRot );
				rot = *D3DXQuaternionNormalize( &tempRot, &rot );

				//回転軸を変換
				rot = ConvertRootRotationAxis( rot, rAxis );

				//指定されている軸の回転を取り出す
				angles = Mix::Tool::Win32::ToEulerRadianZYX( rot );

				D3DXMatrixIdentity( &worldMat );

				if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_DIRECTION_Z ) == MOTION_RT_DIRECTION_Z )
				{
					worldMat *= *D3DXMatrixRotationZ( &tempMat, angles.z );
				}

				if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_DIRECTION_Y ) == MOTION_RT_DIRECTION_Y )
				{
					worldMat *= *D3DXMatrixRotationY( &tempMat, angles.y );
				}

				if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_DIRECTION_X ) == MOTION_RT_DIRECTION_X )
				{
					worldMat *= *D3DXMatrixRotationX( &tempMat, angles.x );
				}

				Mix::Tool::Win32::Matrix_Decompose( worldMat, NULL, &rot, NULL );

				//追加
				m_pRTCurve->AddDirectionKey( time, rot );
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランスレーション
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION ) != 0 ) &&
		( tKeyCount > 0 ) )
	{
		D3DXMATRIX tempMat;
		D3DXMATRIX mat;
		D3DXVECTOR3 basePos;
		D3DXVECTOR3 pos;

		mat = *D3DXMatrixMultiply( &tempMat, &( pNode->GetDefLocalTransform() ), &baseMat );
		Mix::Tool::Win32::Matrix_Decompose( mat, NULL, NULL, &basePos );

		for( unsigned int i = 0; i < tKeyCount; i++ )
		{
			const Mix::Tool::Win32::Graphics::VECTOR_KEY& key = pCurve->GetTranslationKey( i );

			float time = key.time;

			D3DXVECTOR3 localS = pCurve->GetScaling( time );
			D3DXQUATERNION localR = pCurve->GetRotation( time );
			const D3DXVECTOR3& localT = key.value;

			/*
				ローカル行列の作成
			*/

			D3DXMatrixScaling( &mat, localS.x, localS.y, localS.z );
			mat *= *D3DXMatrixRotationQuaternion( &tempMat, &localR );
			mat._41 = localT.x; mat._42 = localT.y; mat._43 = localT.z; mat._44 = 1.0f;

			/*
				ワールド行列に変換し、位置を取得
			*/

			mat = *D3DXMatrixMultiply( &tempMat, &mat, &baseMat );
			Mix::Tool::Win32::Matrix_Decompose( mat, NULL, NULL, &pos );

			/*
				初期座標からのオフセットに直す
			*/

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION_XZ ) == MOTION_RT_TRANSLATION_XZ )
			{
				pos.x -= basePos.x;
				pos.z -= basePos.z;
			}
			else
			{
				pos.x = 0.0f;
				pos.z = 0.0f;
			}

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION_Y ) == MOTION_RT_TRANSLATION_Y )
			{
				pos.y -= basePos.y;
			}
			else
			{
				pos.y = 0.0f;
			}

			/*
				移動軸を変換
			*/

			pos = ConvertRootTranslationAxis( pos, tAxis ); 

			/*
				キーを追加
			*/

			m_pRTCurve->AddTranslationKey( time, pos );
		}
	}

#endif

#if 0

	D3DXQUATERNION baseRotation;
	D3DXMATRIX baseTranslation;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートトランスフォームに必要な情報を取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pNode = m_pObjectModel->GetRootTransformConstant( baseRotation, baseTranslation );
	if( pNode == NULL )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ローテーション
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_ROTATION ) == MOTION_RT_ROTATION ) &&
		( rKeyCount > 0 ) )
	{
		D3DXQUATERNION baseRot = ComputeRootRotation( baseRotation, pNode->GetDefLocalRotation(), rAxis );

		D3DXQuaternionInverse( &baseRot, &baseRot );

		for( unsigned int i = 0; i < rKeyCount; i++ )
		{
			const Mix::Tool::Win32::Graphics::ROTATION_KEY& key = pCurve->GetRotationKey( i );

			D3DXQUATERNION rot = ComputeRootRotation(	baseRotation,
														key.value,
														rAxis );

			D3DXVECTOR3 offset = ComputeRootTranslation(	baseTranslation,
															pCurve->GetScaling( key.time ),
															pCurve->GetRotation( key.time ),
															pCurve->GetTranslation( key.time ),
															tAxis );

			D3DXQuaternionMultiply( &rot, &rot, &baseRot );
			D3DXQuaternionNormalize( &rot, &rot );

			m_pRTCurve->AddRotationKey( static_cast<int>( key.time ), offset, rot );
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランスレーション
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION ) != 0 ) &&
		( tKeyCount > 0 ) )
	{
		D3DXVECTOR3 basePos = ComputeRootTranslation(	baseTranslation,
														pNode->GetDefLocalScaling(),
														pNode->GetDefLocalRotation(),
														pNode->GetDefLocalTranslation(),
														tAxis );

		for( unsigned int i = 0; i < tKeyCount; i++ )
		{
			const Mix::Tool::Win32::Graphics::TRANSLATION_KEY& key = pCurve->GetTranslationKey( i );

			D3DXVECTOR3 pos = ComputeRootTranslation(	baseTranslation,
														pCurve->GetScaling( key.time ),
														pCurve->GetRotation( key.time ),
														pCurve->GetTranslation( key.time ),
														tAxis );

			D3DXVec3Subtract( &pos, &pos, &basePos );

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION_XZ ) == MOTION_RT_TRANSLATION_XZ )
			{
				pos.x = pos.x - basePos.x;
				pos.z = pos.z - basePos.z;
			}
			else
			{
				pos.x = 0.0f;
				pos.z = 0.0f;
			}

			if( MIX_TEST_BIT( m_RTFlags, MOTION_RT_TRANSLATION_Y ) == MOTION_RT_TRANSLATION_Y )
			{
				pos.y = pos.y - basePos.y;
			}
			else
			{
				pos.y = 0.0f;
			}

			m_pRTCurve->AddTranslationKey( static_cast<int>( key.time ), pos );
		}
	}

#endif

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ
	////////////////////////////////////////////////////////////////////////////////////////////////////

#if 0

	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );
	LogPrint( LT_INFO, L" ルートトランスフォームの更新 : Name[%s] LinkNode[%s]", m_Name.c_str(), pCurve->GetLinkNodeName() );
	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );

	if( m_pRTCurve->GetRotationKeyCount() > 0 )
	{
		for( unsigned int i = 0; i < m_pRTCurve->GetRotationKeyCount(); i++ )
		{
			const Mix::Tool::Win32::Graphics::QUATERNION_KEY& key = m_pRTCurve->GetRotationKey( i );
			D3DXVECTOR3 angles = Mix::Tool::Win32::ToEulerDegreeZYX( key.value );

			LogPrint( LT_INFO, L"Rotation : (%f,%f,%f)", angles.x, angles.y, angles.z );
		}
	}
	else
	{
		LogPrint( LT_INFO, L"Rotation : キーはありません" );
	}
/*
	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );

	if( m_pRTCurve->GetDirectionKeyCount() > 0 )
	{
		for( unsigned int i = 0; i < m_pRTCurve->GetDirectionKeyCount(); i++ )
		{
			const Mix::Tool::Win32::Graphics::QUATERNION_KEY& key = m_pRTCurve->GetDirectionKey( i );
			D3DXVECTOR3 angles = Mix::Tool::Win32::ToEulerDegreeZYX( key.value );

			LogPrint( LT_INFO, L"Direction : (%f,%f,%f)", angles.x, angles.y, angles.z );
		}
	}
	else
	{
		LogPrint( LT_INFO, L"Direction : キーはありません" );
	}
*/
	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );

	if( m_pRTCurve->GetTranslationKeyCount() > 0 )
	{
		for( unsigned int i = 0; i < m_pRTCurve->GetTranslationKeyCount(); i++ )
		{
			const D3DXVECTOR3& pos = m_pRTCurve->GetTranslationKey( i ).value;

			LogPrint( LT_INFO, L"Translation : (%f,%f,%f)", pos.x, pos.y, pos.z );
		}
	}
	else
	{
		LogPrint( LT_INFO, L"Translation : キーはありません" );
	}

	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );
#endif
}

Mix::Tool::Win32::Graphics::RootMotionCurve* Motion::GetRootTransformCurvePtr( void ) const
{
	return m_pRTCurve;
}

D3DXQUATERNION Motion::ConvertRootRotationAxis( const D3DXQUATERNION& rotation, const MOTION_RT_AXIS& axis )
{
	D3DXQUATERNION temp;
	D3DXVECTOR3 rotAxis;
	float rotAngle;
	float table[6];

	D3DXQuaternionToAxisAngle( &rotation, &rotAxis, &rotAngle );

	table[MOTION_RT_NEGATIVE_X] = -rotAxis.x;
	table[MOTION_RT_POSITIVE_X] = +rotAxis.x;
	table[MOTION_RT_NEGATIVE_Y] = -rotAxis.y;
	table[MOTION_RT_POSITIVE_Y] = +rotAxis.y;
	table[MOTION_RT_NEGATIVE_Z] = -rotAxis.z;
	table[MOTION_RT_POSITIVE_Z] = +rotAxis.z;

	rotAxis.x = table[axis.x];
	rotAxis.y = table[axis.y];
	rotAxis.z = table[axis.z];

	return *D3DXQuaternionRotationAxis( &temp, &rotAxis, rotAngle );
}

D3DXVECTOR3 Motion::ConvertRootTranslationAxis( const D3DXVECTOR3& translation, const MOTION_RT_AXIS& axis )
{
	float table[6];

	table[MOTION_RT_NEGATIVE_X] = -translation.x;
	table[MOTION_RT_POSITIVE_X] = +translation.x;
	table[MOTION_RT_NEGATIVE_Y] = -translation.y;
	table[MOTION_RT_POSITIVE_Y] = +translation.y;
	table[MOTION_RT_NEGATIVE_Z] = -translation.z;
	table[MOTION_RT_POSITIVE_Z] = +translation.z;

	return D3DXVECTOR3( table[axis.x], table[axis.y], table[axis.z] );
}
/*
D3DXQUATERNION Motion::ComputeRootRotation(	const D3DXQUATERNION& baseRotation,
											const D3DXQUATERNION& rotation,
											const MOTION_RT_AXIS& axis )
{
	D3DXQUATERNION temp;
	D3DXVECTOR3 rotAxis;
	float rotAngle;
	float table[6];

	D3DXQuaternionMultiply( &temp, &rotation, &baseRotation );
	D3DXQuaternionNormalize( &temp, &temp );

	D3DXQuaternionToAxisAngle( &temp, &rotAxis, &rotAngle );

	table[MOTION_RT_NEGATIVE_X] = -rotAxis.x;
	table[MOTION_RT_POSITIVE_X] = +rotAxis.x;
	table[MOTION_RT_NEGATIVE_Y] = -rotAxis.y;
	table[MOTION_RT_POSITIVE_Y] = +rotAxis.y;
	table[MOTION_RT_NEGATIVE_Z] = -rotAxis.z;
	table[MOTION_RT_POSITIVE_Z] = +rotAxis.z;

	rotAxis.x = table[axis.x];
	rotAxis.y = table[axis.y];
	rotAxis.z = table[axis.z];

	return *D3DXQuaternionRotationAxis( &temp, &rotAxis, rotAngle );
}

D3DXVECTOR3 Motion::ComputeRootTranslation( const D3DXMATRIX& baseTranslation,
											const D3DXVECTOR3& scaling,
											const D3DXQUATERNION& rotation,
											const D3DXVECTOR3& translation,
											const MOTION_RT_AXIS& axis )
{
	D3DXMATRIX mat;
	D3DXMATRIX rotMat;
	float table[6];

	D3DXMatrixIdentity( &mat );

	D3DXMatrixScaling( &mat, scaling.x, scaling.y, scaling.z );

	D3DXMatrixIdentity( &rotMat );
	D3DXMatrixRotationQuaternion( &rotMat, &rotation );
	D3DXMatrixMultiply( &mat, &mat, &rotMat );

	mat._41 = translation.x; 
	mat._42 = translation.y;
	mat._43 = translation.z;
	mat._44 = 1.0f;

	D3DXMatrixMultiply( &mat, &mat, &baseTranslation );

	table[MOTION_RT_NEGATIVE_X] = -mat._41;
	table[MOTION_RT_POSITIVE_X] = +mat._41;
	table[MOTION_RT_NEGATIVE_Y] = -mat._42;
	table[MOTION_RT_POSITIVE_Y] = +mat._42;
	table[MOTION_RT_NEGATIVE_Z] = -mat._43;
	table[MOTION_RT_POSITIVE_Z] = +mat._43;

	return D3DXVECTOR3( table[axis.x], table[axis.y], table[axis.z] );
}
*/
void Motion::SetSaveFilePath( const wchar_t* pFilePath )
{
	m_SaveFilePath = ( pFilePath != NULL )? pFilePath : L"";
}

const wchar_t* Motion::GetSaveFilePath( void ) const
{
	return m_SaveFilePath.c_str();
}

void Motion::Play( void )
{
	m_pController->PlayMotion( this );
}

void Motion::Pause( void )
{
	m_pController->PauseMotion( this );
}

void Motion::Stop( void )
{
	m_pController->StopMotion( this );
}

Mix::Tool::Win32::Graphics::MOTION_STATE Motion::GetState( void ) const
{
	return m_pController->GetMotionState( this );
}

void Motion::SetTime( int time )
{
	m_pController->SetMotionTime( this, time );
}

int Motion::GetTime( void )
{
	return m_pController->GetMotionTime( this );
}

void Motion::SetLoop( bool state )
{
	m_bLoop = state;
}

bool Motion::GetLoop( void ) const
{
	return m_bLoop;
}

void Motion::Finish( void )
{
	MIX_ASSERT( m_pObjectModel != NULL );
/*
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// カーブを仕上げる
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( Motion::CurveList::iterator it = m_CurveList.begin(); it != m_CurveList.end(); ++it )
	{
		( *it )->Finish( m_pObjectModel );
	}
*/
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 最後の時間を設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_LastTime = 0.0f;

	for( Motion::CurveList::iterator it = m_CurveList.begin(); it != m_CurveList.end(); ++it )
	{
		float lastTime = ( *it )->GetLastTime();

		if( m_LastTime < lastTime ) { m_LastTime = lastTime; }
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ループの初期値を背低
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_LoopStartTime = 0.0f;
	m_LoopEndTime = m_LastTime;
}
/*
bool Motion::Load( const wchar_t* pFilePath )
{
	MIX_ASSERT( pFilePath != NULL );

	Mix::Tool::Win32::File::InputStream input;
	Motion::MOT_DESC desc;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 解放
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Release();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルを開く
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( input.Open( pFilePath ) == false )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マジックナンバー
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		unsigned int magicNumber;

		if( input.Read( &magicNumber, sizeof( magicNumber ) ) == false )
		{
			return false;
		}

		if( magicNumber != Motion::MOT_MAGIC_NUMBER )
		{
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( input.Read( &desc, sizeof( desc ) ) == false )
	{
		return false;
	}

	if( desc.version != Motion::MOT_VERSION )
	{
		return false;
	}

	m_Name = desc.name;
	m_BlendRatio = desc.blendRatio;
	m_FramesPerSec = desc.framesPerSec;
	m_LastTime = desc.lastFrame;
	m_LoopStartTime = desc.loopStartFrame;
	m_LoopEndTime = desc.loopEndFrame;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートトランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pRTCurve->Read( input, desc.rtRotationKeyNum, desc.rtTranslationKeyNum ) == false )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボディトランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_CurveList.reserve( desc.btCurveNum );

	for( unsigned int i = 0; i < desc.btCurveNum; i++ )
	{
		Mix::Tool::Win32::Graphics::MotionCurve* pCurve = new Mix::Tool::Win32::Graphics::MotionCurve( static_cast<int>( i ) );
		if( pCurve == NULL )
		{
			return false;
		}

		if( pCurve->Read( input ) == true )
		{
			m_CurveList.push_back( pCurve );
		}
		else
		{
			MIX_DELETE( pCurve );
			Release();
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルを閉じる
	////////////////////////////////////////////////////////////////////////////////////////////////////

	input.Close();

	return true;
}
*/
bool Motion::Save( void )
{
	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );

	if( m_SaveFilePath.size() == 0 )
	{
		LogPrint( LT_WARNING, L"  モーションを保存できませんでした : ファイルパスが指定されていません : Location[%s/%s]", m_pController->GetName(), m_Name.c_str() );
		return true;
	}

	Mix::Tool::Win32::File::OutputStream output;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ディレクトリを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Mix::Tool::Win32::Directory::Create( m_SaveFilePath.c_str(), true ) == false )
	{
		LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : ディレクトリの作成中にエラーが発生 : Location[%s/%s] FilePath[%s]",
					m_pController->GetName(),
					m_Name.c_str(),
					m_SaveFilePath.c_str() );

		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルを開く
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( output.Open( m_SaveFilePath.c_str() ) == false )
	{
		LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : ファイルの作成中にエラーが発生 : Location[%s/%s] FilePath[%s]",
					m_pController->GetName(),
					m_Name.c_str(),
					m_SaveFilePath.c_str() );

		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルヘッダ : マジックナンバーを書き込む
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		unsigned int magicNumber = Motion::MOT_MAGIC_NUMBER;

		if( output.Write( &magicNumber, sizeof( magicNumber ) ) == false )
		{
			LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : 書き込み中にエラーが発生 : Location[%s/%s] FilePath[%s]",
						m_pController->GetName(),
						m_Name.c_str(),
						m_SaveFilePath.c_str() );

			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// データを書き込む
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Write( m_SaveFilePath.c_str(), output ) == false )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルを閉じる
	////////////////////////////////////////////////////////////////////////////////////////////////////

	output.Close();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	LogPrint(	LT_INFO, L"  モーションを保存しました : Location[%s/%s] FilePath[%s]",
				m_pController->GetName(),
				m_Name.c_str(),
				m_SaveFilePath.c_str() );

	return true;
}

bool Motion::Write( const wchar_t* pFilePath, Mix::Tool::Win32::File::OutputStream& output )
{
	const wchar_t* rtNodeName = m_pObjectModel->GetRootTransformNodeName();
	MOTION_RT_HORIZONAL rtHoriz = m_pObjectModel->GetRootTranslationHorizonal();
	MOTION_RT_VERTICAL rtVert = m_pObjectModel->GetRootTranslationVertical();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルヘッダ : バージョン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		unsigned int version = Motion::MOT_VERSION;

		if( output.Write( &version, sizeof( version ) ) == false )
		{
			LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : 書き込み中にエラーが発生 : Location[%s/%s] FilePath[%s]",
						m_pController->GetName(),
						m_Name.c_str(),
						pFilePath );

			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// データヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		Motion::MOT_DATA_HEADER dataHeader;

		if( m_Name.size() < MAX_NAME_SIZE )
		{
			::wcscpy_s( dataHeader.name, sizeof( dataHeader.name ) >> 1, m_Name.c_str() );
		}
		else
		{
			LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : 名前は%d文字以下にしてください : Location[%s/%s] FilePath[%s]",
						_MAX_NAME_SIZE,
						m_pController->GetName(),
						m_Name.c_str(),
						pFilePath );

			return false;
		}

		dataHeader.framesPerSec = m_FramesPerSec;
		dataHeader.lastFrame = m_LastTime;
		dataHeader.speed = m_Speed;
		dataHeader.blendRatio = m_BlendRatio;
		dataHeader.loopStartFrame = m_LoopStartTime;
		dataHeader.loopEndFrame = m_LoopEndTime;
		dataHeader.rtCurveNum = ( m_pRTCurve->ExistsKey() == true )? 1 : 0;
		dataHeader.btCurveNum = 0;

		for( Motion::CurveList::iterator it = m_CurveList.begin(); it != m_CurveList.end(); ++it )
		{
			Mix::Tool::Win32::Graphics::MotionCurve* pCurve = ( *it );

			if( pCurve->GetEnabled() == false )
			{
				continue;
			}

			dataHeader.btCurveNum++;
		}

		if( output.Write( &dataHeader, sizeof( dataHeader ) ) == false )
		{
			LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : 書き込み中にエラーが発生 : Location[%s/%s] FilePath[%s]",
						m_pController->GetName(),
						m_Name.c_str(),
						pFilePath );

			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートトランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pRTCurve->ExistsKey() == true )
	{
		if( m_pRTCurve->Write( output ) == false )
		{
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボディトランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( Motion::CurveList::iterator it = m_CurveList.begin(); it != m_CurveList.end(); ++it )
	{
		Mix::Tool::Win32::Graphics::MotionCurve* pCurve = ( *it );

		if( pCurve->GetEnabled() == false )
		{
			continue;
		}

		bool bSuccess;

		if( ( rtNodeName != NULL ) &&
			( ::wcscmp( pCurve->GetLinkNodeName(), rtNodeName ) == 0 ) )
		{
			const Mix::Tool::Win32::Graphics::Node* pRTNode = m_pObjectModel->GetRootTransformNodePtr();

			const D3DXQUATERNION& rtDefR = pRTNode->GetDefLocalRotation();
			const D3DXVECTOR3& rtDefT = pRTNode->GetDefLocalTranslation();

			bSuccess = pCurve->Write(	output,
										m_RTFlags,
										rtHoriz,
										rtVert,
										rtDefR,
										rtDefT );
		}
		else
		{
			bSuccess = pCurve->Write(	output,
										0,
										rtHoriz,
										rtVert,
										D3DXQUATERNION( 0.0f, 0.0f, 0.0f, 1.0f ),
										D3DXVECTOR3( 0.0f, 0.0f, 0.0f ) );
		}

		if( bSuccess == false )
		{
			LogPrint(	LT_ERROR, L"  モーションの保存に失敗しました : 書き込み中にエラーが発生 : Location[%s/%s] FilePath[%s]",
						m_pController->GetName(),
						m_Name.c_str(),
						pFilePath );

			return false;
		}
	}

	return true;
}

void Motion::Destroy( void )
{
	SetController( NULL );

	delete this;
}

Mix::Tool::Win32::Object::TYPE Motion::GetType( void ) const
{
	return Mix::Tool::Win32::Object::GRAPHICS__MOTION;
}

}}}}
