www.pudn.com > DirectX source.zip > CAnimationNode.cpp


/*! 
	@file : CAnimationNode.cpp 
	Contains the implementation of class CAnimationNode 
 
	@author Sarmad Kh. Abdulla 
*/ 
//------------------------------------------------------------------------------ 
 
 
//////////////////////////////////////////////////////////////////////////////// 
// Header files 
#include "stdafx.h" 
 
 
/*! 
 * Load data from the x file. The data loaded are the animation keys of this animation 
 * object. 
 * 
 * @param pxofobj : The x file data object to load from 
 * 
 * @return void  :  
 */ 
void CAnimationNode::Load( LPDIRECTXFILEOBJECT pxofobj ) 
{ 
	// define datatypes needed by the load function 
	struct tmpPOSITIONKEY 
	{ 
		DWORD dwTime; 
		DWORD dwFloatsCount; 
		D3DXVECTOR3 Pos; 
	} * ppositionkey; 
	struct tmpSCALEKEY 
	{ 
		DWORD dwTime; 
		DWORD dwFloatsCount; 
		D3DXVECTOR3 Scale; 
	} * pscalekey; 
	struct tmpROTATIONKEY 
	{ 
		DWORD dwTime; 
		DWORD dwFloatsCount; 
		//NOTE x files are w x y z and QUATERNIONS are x y z w 
		float w; 
		float x; 
		float y; 
		float z; 
	} * protationkey; 
	struct tmpMATRIXKEY 
	{ 
		DWORD dwTime; 
		DWORD dwFloatsCount; 
		D3DXMATRIX Mat; 
	} * pmatrixkey; 
 
	HRESULT result; 
 
	LPDIRECTXFILEDATA pxofdataobj = NULL; 
	LPDIRECTXFILEDATAREFERENCE pxofrefobj = NULL; 
	const GUID *objecttype; 
	DWORD dwsize; 
	PBYTE pdata; 
	DWORD dwkeytype; 
	DWORD dwkeyscount; 
	DWORD dwkey; 
	DWORD dwnamelength; 
	char *szframename; 
 
	// Query the child for it's FileDataReference 
	result = pxofobj->QueryInterface( IID_IDirectXFileDataReference, (LPVOID*)&pxofrefobj ); 
	if( SUCCEEDED( result ) ) 
	{ 
		// The object is of type reference. Use it to get a reference to the target 
		// frame 
		result = pxofrefobj->Resolve( &pxofdataobj ); 
		if( SUCCEEDED( result ) ) 
		{ 
			// The original data is found 
			result = pxofdataobj->GetType( &objecttype ); 
			if( FAILED( result ) ) 
			{ 
				pxofrefobj->Release(); 
				pxofdataobj->Release(); 
				throw new CDXException( "Couldn't retrieve object type while loading animation", 
					result ); 
			} 
 
			// The object must be a frame 
			if( TID_D3DRMFrame == *objecttype ) 
			{ 
				// The frame is found, get its name 
				// The name will be used later by the FindBone function to get 
				// a pointer to the target frame 
 
				if( pTargetFrame != NULL ) 
				{ 
					pxofrefobj->Release(); 
					pxofdataobj->Release(); 
					throw new CCustomException( "Animation frame name reference duplicated" ); 
				} 
 
				// get the length of the name 
				result = pxofdataobj->GetName(NULL, &dwnamelength); 
				if( FAILED( result ) ) 
				{ 
					pxofrefobj->Release(); 
					pxofdataobj->Release(); 
					throw new CDXException( "Couldn't retrieve frame's name while loading animation", 
						result ); 
				} 
 
				if( dwnamelength == 0 ) 
				{ 
					pxofrefobj->Release(); 
					pxofdataobj->Release(); 
					throw new CCustomException( "No name found for an animation frame" ); 
				} 
 
				// allocate memory for the name 
				szframename = new char[dwnamelength]; 
				if( szframename == NULL ) 
				{ 
					pxofrefobj->Release(); 
					pxofdataobj->Release(); 
					throw new CMemoryException(); 
				} 
 
				// get the name 
				result = pxofdataobj->GetName(szframename, &dwnamelength); 
				if( FAILED( result ) ) 
				{ 
					pxofrefobj->Release(); 
					pxofdataobj->Release(); 
					throw new CDXException( "Couldn't retrieve frame's name while loading animation", 
						result ); 
				} 
 
				TargetName = szframename; 
			} 
			SAFERELEASE( pxofdataobj ); 
		} 
		SAFERELEASE( pxofrefobj ); 
	} 
	else 
	{ 
		// Query the child for it's FileData 
		result = pxofobj->QueryInterface( IID_IDirectXFileData, (LPVOID*)&pxofdataobj ); 
		if( SUCCEEDED( result ) ) 
		{ 
			// a data object is found, get its type 
			result = pxofdataobj->GetType( &objecttype ); 
			if( FAILED( result ) ) 
			{ 
				pxofdataobj->Release(); 
				throw new CDXException( "Couldn't retrieve object type", result ); 
			} 
 
			if( TID_D3DRMAnimationKey == *objecttype ) 
			{ 
				// an animation key is found 
				// load the data 
				result = pxofdataobj->GetData( NULL, &dwsize, (PVOID*)&pdata ); 
				if( FAILED( result ) ) 
				{ 
					pxofdataobj->Release(); 
					throw new CDXException( "Couldn't retrieve data", result ); 
				} 
 
				// get the type and count of the key 
				dwkeytype = ((DWORD*)pdata)[0]; 
				dwkeyscount = ((DWORD*)pdata)[1]; 
 
				if( dwkeytype == 0 /*rotation key*/ ) 
				{ 
					if( aRotationKey != NULL ) 
					{ 
						pxofdataobj->Release(); 
						throw new CCustomException( "Rotation key duplicated" ); 
					} 
 
					aRotationKey = new BONEROTATIONKEY[dwkeyscount]; 
					if( !aRotationKey ) 
					{ 
						pxofdataobj->Release(); 
						throw new CMemoryException(); 
					} 
 
					dwRotationKeyCount = dwkeyscount; 
					//NOTE x files are w x y z and QUATERNIONS are x y z w 
 
					protationkey =  (tmpROTATIONKEY*)(pdata + (sizeof(DWORD) * 2)); 
					for( dwkey = 0; dwkey < dwkeyscount; dwkey++ ) 
					{ 
						aRotationKey[dwkey].dwTime = protationkey->dwTime; 
						aRotationKey[dwkey].Rotation.x = protationkey->x; 
						aRotationKey[dwkey].Rotation.y = protationkey->y; 
						aRotationKey[dwkey].Rotation.z = protationkey->z; 
						aRotationKey[dwkey].Rotation.w = protationkey->w; 
                     
						protationkey++; 
					} 
				} 
				else if( dwkeytype == 1 /*scale key*/ ) 
				{ 
					if( aScaleKey != NULL ) 
					{ 
						pxofdataobj->Release(); 
						throw new CCustomException( "Scale key duplicated" ); 
					} 
 
					aScaleKey = new BONESCALEKEY[dwkeyscount]; 
					if( !aScaleKey ) 
					{ 
						pxofdataobj->Release(); 
						throw new CMemoryException(); 
					} 
 
					dwScaleKeyCount = dwkeyscount; 
 
					pscalekey = (tmpSCALEKEY*)(pdata + (sizeof(DWORD) * 2)); 
					for( dwkey = 0; dwkey < dwkeyscount; dwkey++ ) 
					{ 
						aScaleKey[dwkey].dwTime = pscalekey->dwTime; 
						aScaleKey[dwkey].Scale = pscalekey->Scale; 
                     
						pscalekey++; 
					} 
				} 
				else if( dwkeytype == 2 /*position key*/ ) 
				{ 
					if( aPositionKey != NULL ) 
					{ 
						pxofdataobj->Release(); 
						throw new CCustomException( "Position key duplicated" ); 
					} 
 
					aPositionKey = new BONEPOSITIONKEY[dwkeyscount]; 
					if( !aPositionKey ) 
					{ 
						pxofdataobj->Release(); 
						throw new CMemoryException(); 
					} 
 
					dwPositionKeyCount = dwkeyscount; 
 
					ppositionkey = (tmpPOSITIONKEY*)(pdata + (sizeof(DWORD) * 2)); 
					for( dwkey = 0; dwkey < dwkeyscount; dwkey++ ) 
					{ 
						aPositionKey[dwkey].dwTime = ppositionkey->dwTime; 
						aPositionKey[dwkey].Pos = ppositionkey->Pos; 
 
						ppositionkey++; 
					} 
				} 
				else if( dwkeytype == 4 /*matrix key*/ ) 
				{ 
					if( aMatrixKey != NULL ) 
					{ 
						pxofdataobj->Release(); 
						throw CCustomException( "Matrix key duplicated" ); 
					} 
                 
					aMatrixKey = new BONEMATRIXKEY[dwkeyscount]; 
					if( !aMatrixKey ) 
					{ 
						pxofdataobj->Release(); 
						throw new CMemoryException(); 
					} 
 
					dwMatrixKeyCount = dwkeyscount; 
 
					pmatrixkey = (tmpMATRIXKEY*)(pdata + (sizeof(DWORD) * 2)); 
					for( dwkey = 0; dwkey < dwkeyscount; dwkey++ ) 
					{ 
						aMatrixKey[dwkey].dwTime = pmatrixkey->dwTime; 
						aMatrixKey[dwkey].Mat = pmatrixkey->Mat; 
                     
						pmatrixkey++; 
					} 
				} 
				else 
				{ 
					// the type is unknown, report the error 
					pxofdataobj->Release(); 
					throw new CCustomException( "Unexpected animation key type" ); 
				} 
			} 
			SAFERELEASE( pxofdataobj ); 
		} 
	} 
} 
 
 
/*! 
 * Destroy the object 
 * 
 * @param void :  
 * 
 * @return void  :  
 */ 
void CAnimationNode::Destroy( void ) 
{ 
	SAFEDELETEARRAY( aMatrixKey ); 
	SAFEDELETEARRAY( aPositionKey ); 
	SAFEDELETEARRAY( aRotationKey ); 
	SAFEDELETEARRAY( aScaleKey ); 
 
	// destroy the child objects 
	DeleteChilds(); 
} 
 
 
/*! 
 * Find the target bone of this animation. 
 * 
 * @param prootframe : The root frame from which to start the search for the bone 
 * 
 * @return void  :  
 */ 
void CAnimationNode::FindBone( CFrameNode * prootframe ) 
{ 
	if( TargetName != "" ) 
		pTargetFrame = prootframe->FindFrame( TargetName.c_str() ); 
 
	// find the bones of the child objects 
	CAnimationNode * panimation = static_cast(GetFirstChild()); 
	while( panimation ) 
	{ 
		panimation->FindBone( prootframe ); 
		panimation = static_cast(panimation->GetNextSibling()); 
	} 
} 
 
 
/*! 
 * Animate the bones according to the given time. 
 * 
 * @param dwtime : The requested time. 
 * 
 * @return void  :  
 */ 
void CAnimationNode::SetTime( DWORD dwtime ) 
{ 
	UINT ikey; 
	UINT ikey1; 
	UINT ikey2; 
	D3DXMATRIX resultmatrix; 
	D3DXMATRIX tempmatrix; 
	float flerpvalue; 
	D3DXVECTOR3 scalevector; 
	D3DXVECTOR3 positionvector; 
	D3DXQUATERNION quat; 
	BOOL banimate = false; 
	DWORD dwtime1, dwtime2; 
 
	// if no target frame is found, then this object doesn't hold animation keys 
	if( pTargetFrame ) 
	{ 
		// check the type of the key 
		if( aMatrixKey ) 
		{ 
			dwtime %= aMatrixKey[dwMatrixKeyCount-1].dwTime; 
 
			// get the two keys between which the time is currently in 
			for( ikey = 0 ; ikey < dwMatrixKeyCount; ikey++ ) 
				if( aMatrixKey[ikey].dwTime > dwtime ) 
				{ 
					ikey2 = ikey; 
					if( ikey > 0 ) 
						ikey1 = ikey - 1; 
					else  // when ikey == 0, then ikey1 == 0 
						ikey1 = ikey; 
					break; 
				} 
			dwtime1 = aMatrixKey[ikey1].dwTime; 
			dwtime2 = aMatrixKey[ikey2].dwTime; 
 
			// get the lerp value from the time 
			if( (dwtime2-dwtime1) ==0 ) 
				flerpvalue = 0; 
			else 
				flerpvalue = float(dwtime-dwtime1) / float(dwtime2-dwtime1); 
 
			// we cannot apply a lerp function on two matrices, so just get the 
			// nearest one 
			if (flerpvalue > 0.5) 
				ikey = ikey2; 
			else 
				ikey = ikey1; 
 
			// apply the transformation 
			pTargetFrame->SetTransformationMatrix( &aMatrixKey[ikey].Mat ); 
		} 
		else 
		{ 
			// initialize a matrix to hold the result of the transformation 
			D3DXMatrixIdentity(&resultmatrix); 
 
			// check the scale key 
			if( aScaleKey ) 
			{ 
				ikey1 = ikey2 = 0; 
				dwtime %= aScaleKey[dwScaleKeyCount-1].dwTime; 
 
				// get the two keys between which the time is currently in 
				for( ikey = 0; ikey < dwScaleKeyCount; ikey++ ) 
					if( aScaleKey[ikey].dwTime > dwtime ) 
					{ 
						ikey2 = ikey; 
 
						if( ikey > 0 ) 
							ikey1 = ikey - 1; 
						else  // when ikey == 0, then dwp2 == 0 
							ikey1 = ikey; 
						break; 
					} 
				dwtime1 = aScaleKey[ikey1].dwTime; 
				dwtime2 = aScaleKey[ikey2].dwTime; 
 
				// get the lerp value 
				if( (dwtime2-dwtime1) == 0 ) 
					flerpvalue = 0; 
				else 
					flerpvalue =  float(dwtime-dwtime1)/float(dwtime2-dwtime1); 
 
				// apply the lerp function on the scale vector 
				D3DXVec3Lerp( &scalevector, &aScaleKey[ikey1].Scale, 
					&aScaleKey[ikey2].Scale, flerpvalue ); 
 
				// create the matrix 
				D3DXMatrixScaling( &tempmatrix, scalevector.x, scalevector.y, scalevector.z ); 
				D3DXMatrixMultiply( &resultmatrix, &resultmatrix, &tempmatrix ); 
 
				banimate = true; 
			} 
 
			//check rot keys 
			if (aRotationKey ) 
			{ 
				ikey1 = ikey2 = 0; 
				dwtime %= aRotationKey[dwRotationKeyCount-1].dwTime; 
 
				// get the two keys surrounding the current time value 
				for( ikey = 0; ikey < dwRotationKeyCount; ikey++ ) 
					if( aRotationKey[ikey].dwTime > dwtime ) 
					{ 
						ikey2 = ikey; 
						if( ikey > 0 ) 
							ikey1 = ikey - 1; 
						else  // when ikey == 0, then dwp2 == 0 
							ikey1 = ikey; 
						break; 
					} 
				dwtime1 = aRotationKey[ikey1].dwTime; 
				dwtime2 = aRotationKey[ikey2].dwTime; 
 
				// get the lerp value 
				if( (dwtime2-dwtime1) == 0 ) 
					flerpvalue = 0; 
				else 
					flerpvalue = float(dwtime-dwtime1)/float(dwtime2-dwtime1); 
 
				//apply spherical lerp function 
				D3DXQUATERNION q1,q2; 
				q1.x = -aRotationKey[ikey1].Rotation.x; 
				q1.y = -aRotationKey[ikey1].Rotation.y; 
				q1.z = -aRotationKey[ikey1].Rotation.z; 
				q1.w = aRotationKey[ikey1].Rotation.w; 
 
				q2.x = -aRotationKey[ikey2].Rotation.x; 
				q2.y = -aRotationKey[ikey2].Rotation.y; 
				q2.z = -aRotationKey[ikey2].Rotation.z; 
				q2.w = aRotationKey[ikey2].Rotation.w; 
 
				D3DXQuaternionSlerp( &quat, &q1, &q2, flerpvalue ); 
 
				// create the matrix 
				D3DXMatrixRotationQuaternion( &tempmatrix, &quat ); 
				D3DXMatrixMultiply( &resultmatrix, &resultmatrix, &tempmatrix ); 
				banimate = true; 
			} 
 
			// check for position keys 
			if( aPositionKey ) 
			{ 
				ikey1 = ikey2 = 0; 
				dwtime %= aPositionKey[dwPositionKeyCount-1].dwTime; 
 
				// get the two keys surrounding the time value 
				for( ikey = 0; ikey < dwPositionKeyCount; ikey++ ) 
					if( aPositionKey[ikey].dwTime > dwtime ) 
					{ 
						ikey2 = ikey1; 
						if( ikey > 0 ) 
							ikey1 = ikey - 1; 
						else  // when ikey == 0, then dwp2 == 0 
							ikey1 = ikey; 
						break; 
					} 
				dwtime1 = aPositionKey[ikey1].dwTime; 
				dwtime2 = aPositionKey[ikey2].dwTime; 
 
				// get the lerp value 
				if( dwtime2-dwtime1 == 0 ) 
					flerpvalue = 0; 
				else 
					flerpvalue = float(dwtime-dwtime1)/float(dwtime2-dwtime1); 
 
				// apply the lerp function 
				D3DXVec3Lerp( (D3DXVECTOR3*)&positionvector, &aPositionKey[ikey1].Pos,  
					&aPositionKey[ikey2].Pos, flerpvalue ); 
 
				// prepare the matrix 
				D3DXMatrixTranslation( &tempmatrix, positionvector.x, positionvector.y, positionvector.z ); 
				D3DXMatrixMultiply( &resultmatrix, &resultmatrix, &tempmatrix ); 
				banimate = true; 
			} 
			else 
			{ 
				// if no position key is found, use the original position of the frame 
				D3DXMATRIX * poriginalmatrix = pTargetFrame->GetOriginalMatrix(); 
				D3DXMatrixTranslation( &tempmatrix, poriginalmatrix->_41,  
					poriginalmatrix->_42, poriginalmatrix->_43 ); 
				D3DXMatrixMultiply( &resultmatrix, &resultmatrix, &tempmatrix ); 
			} 
 
			if (banimate) 
				pTargetFrame->SetTransformationMatrix( &resultmatrix ); 
		} 
	} 
 
	// call the settime of the childs 
	CAnimationNode * panimation = static_cast(GetFirstChild()); 
	while( panimation ) 
	{ 
		panimation->SetTime( dwtime ); 
		panimation = static_cast(panimation->GetNextSibling()); 
	} 
}