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());
}
}