//**************************************************************************** //* MODULE: Gfx //* FILENAME: bonedanim.cpp //* OWNER: Gary Jesdanun //* CREATION DATE: 11/14/2001 //**************************************************************************** /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __PLAT_NGC__ #include #include "sys/ngc/p_dma.h" #include "sys\ngc\p_aram.h" #endif // __PLAT_NGC__ /***************************************************************************** ** DBG Information ** *****************************************************************************/ #ifdef __PLAT_NGC__ #define __ARAM__ #endif #ifdef __PLAT_NGC__ #define _16(a) (((a>>8)&0x00ff)|((a<<8)&0xff00)) #define _32(a) (((a>>24)&0x000000ff)|((a>>8)&0x0000ff00)|((a<<8)&0x00ff0000)|((a<<24)&0xff000000)) #else #define _16(a) a #define _32(a) a #endif // __PLAT_NGC__ namespace Gfx { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ // In order to compress the data, we convert the animation quat into // a unit quat and then discard the W value during export-time/load-time. // Given the X, Y, Z values, we can rebuild the W value at run-time. // The code uses the following tolerance to when comparing the rebuilt W value // to the original value. I chose the number pretty arbitrarily, based // on the existing (THPS3) skater and pedestrian animations. //(0.0005 radians ~= 0.028 degrees) const float nxUNITQUAT_TOLERANCE_RADIANS = 0.0005f; /***************************************************************************** ** Private Types ** *****************************************************************************/ // The following structures are only needed until new data // is exported in the correct format struct SBonedAnimFileHeader { uint32 version; uint32 flags; float duration; }; struct SPlatformFileHeader { uint32 numBones; uint32 numQKeys; uint32 numTKeys; uint32 numCustomAnimKeys; }; struct SStandardAnimFramePointers { unsigned char numQKeys; unsigned char numTKeys; }; struct SHiResAnimFramePointers { short numQKeys; short numTKeys; }; //#define nxBONEDANIMFLAGS_UNUSED (1<<31) #define nxBONEDANIMFLAGS_INTERMEDIATE (1<<30) #define nxBONEDANIMFLAGS_UNCOMPRESSED (1<<29) #define nxBONEDANIMFLAGS_PLATFORM (1<<28) #define nxBONEDANIMFLAGS_CAMERADATA (1<<27) #define nxBONEDANIMFLAGS_COMPRESSEDTIME (1<<26) #define nxBONEDANIMFLAGS_PREROTATEDROOT (1<<25) #define nxBONEDANIMFLAGS_OBJECTANIMDATA (1<<24) #define nxBONEDANIMFLAGS_USECOMPRESSTABLE (1<<23) #define nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS (1<<22) #define nxBONEDANIMFLAGS_CUSTOMKEYSAT60FPS (1<<21) #define nxBONEDANIMFLAGS_CUTSCENEDATA (1<<20) #define nxBONEDANIMFLAGS_PARTIALANIM (1<<19) // to be phased out... #define nxBONEDANIMFLAGS_OLDPARTIALANIM (1<<18) /***************************************************************************** ** Private Data ** *****************************************************************************/ #ifdef __ARAM__ #define ARAM_CACHE_SIZE (64*1024) #define MAX_BONE_COUNT 128 static char qqq[ARAM_CACHE_SIZE] ATTRIBUTE_ALIGN(32); static uint16 framecount[MAX_BONE_COUNT*2] ATTRIBUTE_ALIGN(32); //static uint32 partial[8] ATTRIBUTE_ALIGN(32); volatile uint8 framecount_active = 0; static int framecount_size = 0; ARQRequest framecount_request; #endif /***************************************************************************** ** Public Data ** *****************************************************************************/ // compress tables take up 4K, // which are a waste on Xbox and GC // (should PLAT_NGPS it once it's working) struct CBonedAnimCompressEntry { short x48; short y48; short z48; short n8; }; CBonedAnimCompressEntry sQTable[256]; CBonedAnimCompressEntry sTTable[256]; /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ bool init_48_table( void* pStream, CBonedAnimCompressEntry* pEntry ) { for ( int i = 0; i < 256; i++ ) { if ( !File::Read( (int*)&pEntry->x48, sizeof(short), 1, pStream ) ) { return false; } if ( !File::Read( (int*)&pEntry->y48, sizeof(short), 1, pStream ) ) { return false; } if ( !File::Read( (int*)&pEntry->z48, sizeof(short), 1, pStream ) ) { return false; } if ( !File::Read( (int*)&pEntry->n8, sizeof(short), 1, pStream ) ) { return false; } pEntry->x48 = _16( pEntry->x48 ); pEntry->y48 = _16( pEntry->y48 ); pEntry->z48 = _16( pEntry->z48 ); pEntry->n8 = _16( pEntry->n8 ); pEntry++; } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __ARAM__ //static volatile bool dmaComplete; static void arqCallback( u32 pointerToARQRequest ) { // ARQRequest * p_arq = (ARQRequest *)pointerToARQRequest; // if ( p_arq->owner == 0x55555555 ) // { framecount_active = 0; // } } void dma_count( uint32 p_source_base, int size ) { // DMA the count info. size = ( ( size + 31 ) & ~31 ); DCFlushRange ( framecount, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. p_source_base, // Source. (uint32)framecount, // Dest. size, // Length. arqCallback ); // Callback } //void dma_partial( uint32 p_source_base ) //{ // while ( framecount_active ); // // DMA the count info. // DCFlushRange ( partial, 32 ); // framecount_size = 32; // framecount_active = 1; // ARQPostRequest ( &framecount_request, // 0x55555555, // Owner. // ARQ_TYPE_ARAM_TO_MRAM, // Type. // ARQ_PRIORITY_HIGH, // Priority. // p_source_base, // Source. // (uint32)partial, // Dest. // 32, // Length. // arqCallback ); // Callback //} #endif /******************************************************************/ /* */ /* */ /******************************************************************/ bool InitQ48Table( const char* pFileName, bool assertOnFail ) { bool success = false; // open the file as a stream void* pStream = File::Open( pFileName, "rb" ); // make sure the file is valid if ( !pStream ) { Dbg_MsgAssert( !assertOnFail, ("Load of %s failed - file not found?", pFileName) ); goto exit; } success = init_48_table( pStream, &sQTable[0] ); exit: Dbg_MsgAssert( success, ("Parse of %s failed", pFileName) ); File::Close( pStream ); return success; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool InitT48Table( const char* pFileName, bool assertOnFail ) { bool success = false; // open the file as a stream void* pStream = File::Open( pFileName, "rb" ); // make sure the file is valid if ( !pStream ) { Dbg_MsgAssert( assertOnFail, ("Load of %s failed - file not found?", pFileName) ); goto exit; } success = init_48_table( pStream, &sTTable[0] ); //Dbg_Assert( 0 ); exit: Dbg_MsgAssert( success, ("Parse of %s failed", pFileName) ); File::Close( pStream ); return success; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float timeDown(short theSource) { return (float)theSource; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline short timeUp(float theSource) { short retVal = (short)((theSource * 60.0f) + 0.5f); return retVal; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline short transUp(float theSource, float scaleFactor) { return (short)(theSource * scaleFactor); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float transDown(short theSource, float scaleFactor) { return (float)(theSource / scaleFactor); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline short quatUp(float theSource) { return (short)(theSource * 16384.0f); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float quatDown(short theSource) { return (float)(theSource / 16384.0f); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void get_rotation_from_key( CAnimQKey* p_in, Mth::Quat* pQuat, bool isHiRes ) { if ( isHiRes ) { (*pQuat)[X] = ((CHiResAnimQKey*)p_in)->qx; (*pQuat)[Y] = ((CHiResAnimQKey*)p_in)->qy; (*pQuat)[Z] = ((CHiResAnimQKey*)p_in)->qz; // (*pQuat)[W] = ((CHiResAnimQKey*)p_in)->qw; } else { (*pQuat)[X] = quatDown( ((CStandardAnimQKey*)p_in)->qx ); (*pQuat)[Y] = quatDown( ((CStandardAnimQKey*)p_in)->qy ); (*pQuat)[Z] = quatDown( ((CStandardAnimQKey*)p_in)->qz ); // (*pQuat)[W] = quatDown( ((CStandardAnimQKey*)p_in)->qw ); } float qx = (*pQuat)[X]; float qy = (*pQuat)[Y]; float qz = (*pQuat)[Z]; // Dave note: added 09/12/02 - a simple check to ensure we don't try to take the square root of a negative // number, which will hose Nan-sensitive platforms later on... float sum = 1.0f - qx * qx - qy * qy - qz * qz; (*pQuat)[W] = sqrtf(( sum >= 0.0f ) ? sum : 0.0f ); if ( p_in->signBit ) { (*pQuat)[W] = -(*pQuat)[W]; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void set_key_from_rotation( CAnimQKey* p_in, float x, float y, float z, float w, bool isHiRes ) { if ( isHiRes ) { ((CHiResAnimQKey*)p_in)->qx = x; ((CHiResAnimQKey*)p_in)->qy = y; ((CHiResAnimQKey*)p_in)->qz = z; // ((CHiResAnimQKey*)p_in)->qw = w; ((CHiResAnimQKey*)p_in)->signBit = ( w < 0.0f ) ? 1 : 0; } else { ((CStandardAnimQKey*)p_in)->qx = quatUp( x ); ((CStandardAnimQKey*)p_in)->qy = quatUp( y ); ((CStandardAnimQKey*)p_in)->qz = quatUp( z ); // ((CStandardAnimQKey*)p_in)->qw = quatUp( w ); ((CStandardAnimQKey*)p_in)->signBit = ( w < 0.0f ) ? 1 : 0; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void get_translation_from_key( CAnimTKey* p_in, Mth::Vector* pVector, bool isHiRes ) { if ( isHiRes ) { (*pVector)[X] = ((CHiResAnimTKey*)p_in)->tx; (*pVector)[Y] = ((CHiResAnimTKey*)p_in)->ty; (*pVector)[Z] = ((CHiResAnimTKey*)p_in)->tz; (*pVector)[W] = 1.0f; } else { (*pVector)[X] = transDown( ((CStandardAnimTKey*)p_in)->tx, 32.0f ); (*pVector)[Y] = transDown( ((CStandardAnimTKey*)p_in)->ty, 32.0f ); (*pVector)[Z] = transDown( ((CStandardAnimTKey*)p_in)->tz, 32.0f ); (*pVector)[W] = 1.0f; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void get_rotation_from_standard_key( CStandardAnimQKey* p_in, Mth::Quat* pQuat ) { if ( p_in->qx == 0 && p_in->qy == 0 && p_in->qz == 0 ) { // Optimization: this handles the identity rotation, which is so common that it's not // worth doing a square root... pQuat->SetVector( 0.0f, 0.0f, 0.0f ); pQuat->SetScalar( 1.0f ); return; } float qx = quatDown( p_in->qx ); float qy = quatDown( p_in->qy ); float qz = quatDown( p_in->qz ); // Dave note: added 09/12/02 - a simple check to ensure we don't try // to take the square root of a negative number, which will hose // Nan-sensitive platforms later on... float sum = 1.0f - qx * qx - qy * qy - qz * qz; // This assert was firing off in one of the cutscenes... // probably worth looking into later... // Dbg_Assert( sum >= 0.0f ); if ( sum < 0.0f ) { sum = 0.0f; } (*pQuat)[X] = qx; (*pQuat)[Y] = qy; (*pQuat)[Z] = qz; (*pQuat)[W] = ( p_in->signBit ) ? -sqrtf( sum ) : sqrtf( sum ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void get_translation_from_standard_key( CStandardAnimTKey* p_in, Mth::Vector* pVector ) { (*pVector)[X] = transDown( p_in->tx, 32.0f ); (*pVector)[Y] = transDown( p_in->ty, 32.0f ); (*pVector)[Z] = transDown( p_in->tz, 32.0f ); (*pVector)[W] = 1.0f; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void set_key_from_translation( CAnimTKey* p_in, float x, float y, float z, bool isHiRes ) { if ( isHiRes ) { ((CHiResAnimTKey*)p_in)->tx = x; ((CHiResAnimTKey*)p_in)->ty = y; ((CHiResAnimTKey*)p_in)->tz = z; } else { ((CStandardAnimTKey*)p_in)->tx = transUp( x, 32.0f ); ((CStandardAnimTKey*)p_in)->ty = transUp( y, 32.0f ); ((CStandardAnimTKey*)p_in)->tz = transUp( z, 32.0f ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void interpolate_q_frame(Mth::Quat* p_out, CAnimQKey* p_in1, CAnimQKey* p_in2, float alpha, bool isHiRes) { if ( alpha == 0.0f ) { // don't need to slerp, because it's the start time get_rotation_from_key( p_in1, p_out, isHiRes ); return; } if ( alpha == 1.0f ) { // don't need to slerp, because it's the end time get_rotation_from_key( p_in2, p_out, isHiRes ); return; } // INTERPOLATE Q-COMPONENT Mth::Quat qIn1; Mth::Quat qIn2; get_rotation_from_key( p_in1, &qIn1, isHiRes ); get_rotation_from_key( p_in2, &qIn2, isHiRes ); // Faster slerp, stolen from game developer magazine. *p_out = Mth::FastSlerp( qIn1, qIn2, alpha ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void interpolate_t_frame(Mth::Vector* p_out, CAnimTKey* p_in1, CAnimTKey* p_in2, float alpha, bool isHiRes) { if ( alpha == 0.0f ) { // don't need to lerp, because it's the start time get_translation_from_key( p_in1, p_out, isHiRes ); return; } if ( alpha == 1.0f ) { // don't need to slerp, because it's the end time get_translation_from_key( p_in2, p_out, isHiRes ); return; } // INTERPOLATE T-COMPONENT Mth::Vector tIn1; Mth::Vector tIn2; get_translation_from_key( p_in1, &tIn1, isHiRes ); get_translation_from_key( p_in2, &tIn2, isHiRes ); /* Linearly interpolate positions */ *p_out = Mth::Lerp( tIn1, tIn2, alpha ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void interpolate_standard_q_frame(Mth::Quat* p_out, CStandardAnimQKey* p_in1, CStandardAnimQKey* p_in2, float alpha) { if ( alpha == 0.0f ) { // don't need to slerp, because it's the start time get_rotation_from_standard_key( p_in1, p_out ); return; } if ( alpha == 1.0f ) { // don't need to slerp, because it's the end time get_rotation_from_standard_key( p_in2, p_out ); return; } // INTERPOLATE Q-COMPONENT Mth::Quat qIn1; Mth::Quat qIn2; get_rotation_from_standard_key( p_in1, &qIn1 ); get_rotation_from_standard_key( p_in2, &qIn2 ); // Faster slerp, stolen from game developer magazine. *p_out = Mth::FastSlerp( qIn1, qIn2, alpha ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void interpolate_standard_t_frame(Mth::Vector* p_out, CStandardAnimTKey* p_in1, CStandardAnimTKey* p_in2, float alpha) { if ( alpha == 0.0f ) { // don't need to lerp, because it's the start time get_translation_from_standard_key( p_in1, p_out ); return; } if ( alpha == 1.0f ) { // don't need to slerp, because it's the end time get_translation_from_standard_key( p_in2, p_out ); return; } // INTERPOLATE T-COMPONENT Mth::Vector tIn1; Mth::Vector tIn2; get_translation_from_standard_key( p_in1, &tIn1 ); get_translation_from_standard_key( p_in2, &tIn2 ); /* Linearly interpolate positions */ *p_out = Mth::Lerp( tIn1, tIn2, alpha ); } /******************************************************************/ /* */ /* */ /******************************************************************/ // eventually, this will go in the plat-specific version of CBonedAnimFrameData bool CBonedAnimFrameData::plat_dma_to_aram( int qbytes, int tbytes, uint32 flags ) { // GameCube: DMA to ARAM. #ifdef __ARAM__ if ( qbytes && tbytes ) { uint32 address; uint size; // uint32 header[8]; // // Want this to happen even if asserts turned off. // if ( qbytes > (ARAM_CACHE_SIZE) ) // { // OSReport( "Too many q keys (%d)!!!\n", qbytes ); // while (1); // } // // if ( tbytes > (ARAM_CACHE_SIZE) ) // { // OSReport( "Too many t keys (%d)!!!\n", tbytes ); // while (1); // } // // if ( m_numBones > MAX_BONE_COUNT ) // { // OSReport( "Too many bones (%d)!!!\n", m_numBones ); // while (1); // } // // DMA partial animation data. // if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) // { // address = NsARAM::alloc( 32 ); // NsDMA::toARAM( address, mp_partialAnimData, 32 ); // mp_partialAnimData = (uint32*)address; // } // DMA per-bone frame count data. size = m_numBones * sizeof(uint16) * 2; memcpy( qqq, mp_perBoneQFrameSize, size ); int part_size = 0; int frame_size = size; if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) { part_size = ( 1 + (( *mp_partialAnimData - 1 )/ 32) + 1 ) * sizeof( uint32 ); memcpy( &qqq[size], mp_partialAnimData, part_size ); size += part_size; } size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, qqq, size ); mp_perBoneQFrameSize = (uint16*)address; mp_perBoneTFrameSize = (uint16*)address; if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) { mp_partialAnimData = (uint32*)( address + frame_size ); } // size = m_numBones * sizeof(uint16) * 2; // size = ( ( size + 31 ) & ~31 ); // address = NsARAM::alloc( size ); // NsDMA::toARAM( address, mp_perBoneQFrameSize, size ); // // mp_perBoneQFrameSize = (uint16*)address; // mp_perBoneTFrameSize = (uint16*)address; // DMA Q frames to ARAM, delete RAM version & assign ARAM pointer. size = qbytes/* + 32*/; size = ( ( size + 31 ) & ~31 ); // header[0] = size; address = NsARAM::alloc( size ); // NsDMA::toARAM( address, header, 32 ); NsDMA::toARAM( address/*+32*/, mp_qFrames, size ); // delete mp_qFrames; mp_qFrames = (char*)address; // DMA T frames to ARAM, delete RAM version & assign ARAM pointer. size = tbytes/* + 32*/; size = ( ( size + 31 ) & ~31 ); // header[0] = size; address = NsARAM::alloc( size ); // NsDMA::toARAM( address, header, 32 ); NsDMA::toARAM( address/*+32*/, mp_tFrames, size ); // delete mp_tFrames; mp_tFrames = (char*)address; // hijack this pointer as it won't be used. mp_boneNames = (uint32*)( ( qbytes << 16 ) | tbytes ); } else { if ( is_hires() ) { uint32 address; uint size; // // Want this to happen even if asserts turned off. // if ( ( m_num_qFrames * sizeof( CHiResAnimQKey ) ) > (ARAM_CACHE_SIZE) ) // { // OSReport( "Too many q keys (%d)!!!\n", m_num_qFrames ); // while (1); // } // // if ( ( m_num_tFrames * sizeof( CHiResAnimTKey ) ) > (ARAM_CACHE_SIZE) ) // { // OSReport( "Too many t keys (%d)!!!\n", m_num_tFrames ); // while (1); // } // // // DMA partial animation data. // if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) // { // uint32 * pData = mp_partialAnimData; // int numBones = *pData; // // int numMasks = (( numBones - 1 )/ 32) + 1; // size = sizeof( int ) + ( numMasks * sizeof( uint32 ) ); // // header[0] = size; // address = NsARAM::alloc( size ); // NsDMA::toARAM( address, header, 32 ); // NsDMA::toARAM( address+32, mp_partialAnimData, size ); // mp_partialAnimData = (char*)address; // } mp_partialAnimData = NULL; // DMA per-bone frame count data. void * ptr; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { size = ( m_numBones * sizeof(uint32) ); ptr = mp_boneNames; } else { size = 0; ptr = mp_perBoneFrames; } if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { size += ( m_numBones * sizeof(SHiResAnimFramePointers) ); } else { size += ( m_numBones * sizeof(SStandardAnimFramePointers) ); } if ( size > ( MAX_BONE_COUNT * 2 * sizeof(uint16) ) ) { OSReport( "Too many bones (%d)!!!\n", m_numBones ); while (1); } size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, ptr, size ); mp_perBoneFrames = (uint16*)address; mp_perBoneQFrameSize = (uint16*)address; // So that it can be deallocated. // DMA Q frames to ARAM, delete RAM version & assign ARAM pointer. // Dbg_MsgAssert( m_num_qFrames <= (ARAM_CACHE_SIZE), ( "Too many hires Q keys (%d) - maximum is %d", m_num_qFrames, (ARAM_CACHE_SIZE) ) ); size = sizeof( CHiResAnimQKey ) * m_num_qFrames; size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, mp_qFrames, size ); // delete mp_qFrames; mp_qFrames = (char*)address; // DMA T frames to ARAM, delete RAM version & assign ARAM pointer. // Dbg_MsgAssert( m_num_tFrames <= (ARAM_CACHE_SIZE), ( "Too many hires T keys (%d) - maximum is %d", m_num_tFrames, (ARAM_CACHE_SIZE) ) ); size = sizeof( CHiResAnimTKey ) * m_num_tFrames; size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, mp_tFrames, size ); // delete mp_tFrames; mp_tFrames = (char*)address; } else { uint32 address; uint size; // // Want this to happen even if asserts turned off. // if ( ( m_num_qFrames * sizeof( CStandardAnimQKey ) ) > (ARAM_CACHE_SIZE) ) // { // OSReport( "Too many q keys (%d)!!!\n", m_num_qFrames ); // while (1); // } // // if ( ( m_num_tFrames * sizeof( CStandardAnimTKey ) ) > (ARAM_CACHE_SIZE) ) // { // OSReport( "Too many t keys (%d)!!!\n", m_num_tFrames ); // while (1); // } // // // DMA partial animation data. // if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) // { // uint32 * pData = mp_partialAnimData; // int numBones = *pData; // // int numMasks = (( numBones - 1 )/ 32) + 1; // size = sizeof( int ) + ( numMasks * sizeof( uint32 ) ); // // header[0] = size; // address = NsARAM::alloc( size ); // NsDMA::toARAM( address, header, 32 ); // NsDMA::toARAM( address+32, mp_partialAnimData, size ); // mp_partialAnimData = (char*)address; // } mp_partialAnimData = NULL; // DMA per-bone frame count data. void * ptr; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { size = ( m_numBones * sizeof(uint32) ); ptr = mp_boneNames; } else { size = 0; ptr = mp_perBoneFrames; } if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { size += ( m_numBones * sizeof(SHiResAnimFramePointers) ); } else { size += ( m_numBones * sizeof(SStandardAnimFramePointers) ); } if ( size > ( MAX_BONE_COUNT * 2 * sizeof(uint16) ) ) { OSReport( "Too many bones (%d)!!!\n", m_numBones ); while (1); } size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, ptr, size ); mp_perBoneFrames = (uint16*)address; mp_perBoneQFrameSize = (uint16*)address; // So that it can be deallocated. // DMA Q frames to ARAM, delete RAM version & assign ARAM pointer. // Dbg_MsgAssert( m_num_qFrames <= (short)MAX_STANDARD_Q, ( "Too many standard Q keys (%d) - maximum is %d", m_num_qFrames, MAX_STANDARD_Q ) ); size = sizeof( CStandardAnimQKey ) * m_num_qFrames; size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, mp_qFrames, size ); // delete mp_qFrames; mp_qFrames = (char*)address; // DMA T frames to ARAM, delete RAM version & assign ARAM pointer. // Dbg_MsgAssert( m_num_tFrames <= (short)MAX_STANDARD_T, ( "Too many standard T keys (%d) - maximum is %d", m_num_tFrames, MAX_STANDARD_T ) ); size = sizeof( CStandardAnimTKey ) * m_num_tFrames; size = ( ( size + 31 ) & ~31 ); address = NsARAM::alloc( size ); NsDMA::toARAM( address, mp_tFrames, size ); // delete mp_tFrames; mp_tFrames = (char*)address; } } #endif // __ARAM__ return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::plat_read_compressed_stream(uint8* pData, bool delete_buffer) { Dbg_Assert( pData ); SPlatformFileHeader *pThePlatformHeader = (SPlatformFileHeader *) pData; pData += sizeof(SPlatformFileHeader); m_numBones = pThePlatformHeader->numBones; m_num_qFrames = pThePlatformHeader->numQKeys; m_num_tFrames = pThePlatformHeader->numTKeys; uint32 qAllocSize = *((uint32*)pData); pData += sizeof(uint32); uint32 tAllocSize = *((uint32*)pData); pData += sizeof(uint32); mp_perBoneQFrameSize = (uint16*)pData; pData += ( m_numBones * sizeof(uint16) ); mp_perBoneTFrameSize = (uint16*)pData; pData += ( m_numBones * sizeof(uint16) ); // long-align if ( (uint32)pData & 0x3 ) { pData += ( 4 - ((uint32)pData & 0x3) ); } // first read in object anim bone names, if any if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { Dbg_MsgAssert( 0, ( "Wasn't expecting object anims to be compressed" ) ); mp_boneNames = NULL; } else { mp_boneNames = NULL; } if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) { Dbg_Assert(!((uint32)pData & 0x3)); mp_partialAnimData = (uint32*)pData; // skip original number of bones int numBones = *((uint32*)pData); pData += sizeof( int ); int numMasks = (( numBones - 1 )/ 32) + 1; pData += ( numMasks * sizeof( uint32 ) ); } // long-align if ( (uint32)pData & 0x3 ) { pData += ( 4 - ((uint32)pData & 0x3) ); } if ( is_hires() ) { Dbg_MsgAssert( !is_hires(), ( "Hi res format not supported for compressed anims" ) ); } else { mp_qFrames = (char*)pData; pData += qAllocSize; mp_tFrames = (char*)pData; pData += tAllocSize; } Dbg_Assert( mp_perBoneFrames == NULL ); Dbg_Assert( mp_qFrames ); Dbg_Assert( mp_tFrames ); // dma to aram here... plat_dma_to_aram( qAllocSize, tAllocSize ); // GJ: be able to PIP the custom keys in as well // long-align if ( (uint32)pData & 0x3 ) { pData += ( 4 - ((uint32)pData & 0x3) ); } // create an array of pointers to the custom keys if ( !pThePlatformHeader->numCustomAnimKeys ) { mpp_customAnimKeyList = NULL; } else { mpp_customAnimKeyList = (CCustomAnimKey **)(Mem::Malloc(pThePlatformHeader->numCustomAnimKeys*sizeof(CCustomAnimKey *))); // read custom keys for ( uint32 i = 0; i < pThePlatformHeader->numCustomAnimKeys; i++ ) { CCustomAnimKey* pKey = ReadCustomAnimKey( &pData ); if ( !pKey ) { Dbg_Message( "Failed while reading custom data" ); return false; } mpp_customAnimKeyList[i] = pKey; } } m_num_customKeys = pThePlatformHeader->numCustomAnimKeys; #ifdef __ARAM__ if ( delete_buffer ) { delete mp_fileBuffer; } mp_fileBuffer = NULL; #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::plat_read_stream(uint8* pData, bool delete_buffer) { Dbg_Assert( pData ); SPlatformFileHeader* pThePlatformHeader = (SPlatformFileHeader*)pData; pData += sizeof(SPlatformFileHeader); Dbg_Assert(!((uint) pData & 0x3)); m_numBones = pThePlatformHeader->numBones; m_num_qFrames = pThePlatformHeader->numQKeys; m_num_tFrames = pThePlatformHeader->numTKeys; Dbg_Assert( !mp_perBoneFrames ); Dbg_Assert( !mp_qFrames ); Dbg_Assert( !mp_tFrames ); // first read in object anim bone names, if any if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { Dbg_Assert(!((uint) pData & 0x3)); mp_boneNames = (uint32*)pData; pData += ( m_numBones * sizeof(uint32) ); } else { mp_boneNames = NULL; } if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) { Dbg_Assert(!((uint32)pData & 0x3)); mp_partialAnimData = (uint32*)pData; // skip original number of bones int numBones = *((uint32*)pData); pData += sizeof( int ); int numMasks = (( numBones - 1 )/ 32) + 1; pData += ( numMasks * sizeof( uint32 ) ); } // now, read in the per-bone frames of the correct size if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { mp_perBoneFrames = pData; pData += ( m_numBones * sizeof(SHiResAnimFramePointers) ); } else { mp_perBoneFrames = pData; pData += ( m_numBones * sizeof(SStandardAnimFramePointers) ); } // long-align if ( (uint32)pData & 0x3 ) { pData += ( 4 - ((uint32)pData & 0x3) ); } // count to make sure the number of keys per bone didn't overflow int runningQCount = 0; int runningTCount = 0; for ( int i = 0; i < m_numBones; i++ ) { runningQCount += get_num_qkeys( mp_perBoneFrames, i ); runningTCount += get_num_tkeys( mp_perBoneFrames, i ); } Dbg_MsgAssert( runningQCount == m_num_qFrames, ( "Wrong number of qframes in %x %d %d", m_fileNameCRC, runningQCount, m_num_qFrames ) ); Dbg_MsgAssert( runningTCount == m_num_tFrames, ( "Wrong number of tframes in %x %d %d", m_fileNameCRC, runningTCount, m_num_tFrames ) ); if ( is_hires() ) { Dbg_Assert(!((uint) pData & 0x3)); mp_qFrames = (char*)pData; pData += ( m_num_qFrames * sizeof(CHiResAnimQKey) ); Dbg_Assert(!((uint) pData & 0x3)); mp_tFrames = (char*)pData; pData += ( m_num_tFrames * sizeof(CHiResAnimTKey) ); } else { Dbg_Assert(!((uint) pData & 0x3)); mp_qFrames = (char*)pData; pData += ( m_num_qFrames * sizeof(CStandardAnimQKey) ); Dbg_Assert(!((uint) pData & 0x3)); mp_tFrames = (char*)pData; pData += ( m_num_tFrames * sizeof(CStandardAnimTKey) ); } Dbg_Assert( mp_perBoneFrames ); Dbg_Assert( mp_qFrames ); Dbg_Assert( mp_tFrames ); // dma to aram here... plat_dma_to_aram( 0, 0, m_flags ); // GJ: be able to PIP the custom keys in as well // long-align if ( (uint32)pData & 0x3 ) { pData += ( 4 - ((uint32)pData & 0x3) ); } // create an array of pointers to the custom keys if ( !pThePlatformHeader->numCustomAnimKeys ) { mpp_customAnimKeyList = NULL; } else { mpp_customAnimKeyList = (CCustomAnimKey **)(Mem::Malloc(pThePlatformHeader->numCustomAnimKeys*sizeof(CCustomAnimKey *))); // read custom keys for ( uint32 i = 0; i < pThePlatformHeader->numCustomAnimKeys; i++ ) { CCustomAnimKey* pKey = ReadCustomAnimKey( &pData ); if ( !pKey ) { Dbg_Message( "Failed while reading custom data" ); return false; } mpp_customAnimKeyList[i] = pKey; } } m_num_customKeys = pThePlatformHeader->numCustomAnimKeys; #ifdef __ARAM__ if ( delete_buffer ) { delete mp_fileBuffer; } mp_fileBuffer = NULL; #endif return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ int CBonedAnimFrameData::get_num_qkeys( void * p_frame_data, int boneIndex ) const { if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { SHiResAnimFramePointers* pFramePointers = (SHiResAnimFramePointers*)p_frame_data; pFramePointers += boneIndex; return pFramePointers->numQKeys; } else { SStandardAnimFramePointers* pFramePointers = (SStandardAnimFramePointers*)p_frame_data; pFramePointers += boneIndex; return pFramePointers->numQKeys; } } /******************************************************************/ /* */ /* */ /******************************************************************/ int CBonedAnimFrameData::get_num_tkeys( void * p_frame_data, int boneIndex ) const { if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { SHiResAnimFramePointers* pFramePointers = (SHiResAnimFramePointers*)p_frame_data; pFramePointers += boneIndex; return pFramePointers->numTKeys; } else { SStandardAnimFramePointers* pFramePointers = (SStandardAnimFramePointers*)p_frame_data; pFramePointers += boneIndex; return pFramePointers->numTKeys; } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::is_hires() const { return ( m_flags & nxBONEDANIMFLAGS_CAMERADATA ) || ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ); } /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ CBonedAnimFrameData::CBonedAnimFrameData() { m_duration = 0.0f; m_numBones = 0; mp_fileBuffer = NULL; mp_fileHandle = NULL; m_dataLoaded = false; mp_qFrames = NULL; mp_tFrames = NULL; mp_perBoneFrames = NULL; mp_boneNames = NULL; mp_perBoneQFrameSize = NULL; mp_perBoneTFrameSize = NULL; m_printDebugInfo = false; m_num_customKeys = 0; mpp_customAnimKeyList = NULL; m_fileNameCRC = 0; m_pipped = false; mp_partialAnimData = NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ CBonedAnimFrameData::~CBonedAnimFrameData() { for (int i=0;iGetFileSize(); Dbg_MsgAssert(file_size, ("Anim file size is 0")); Dbg_Assert(!mp_fileBuffer); // Now that we're pipping it, we don't have to force this on the top-down heap // Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); mp_fileBuffer = (void *) Mem::Malloc( file_size ); // Mem::Manager::sHandle().PopContext(); // Set the callback mp_fileHandle->SetCallback(async_callback, (unsigned int) this, (unsigned int) assertOnFail); // read the file in mp_fileHandle->Read( mp_fileBuffer, 1, file_size ); //Dbg_Message("Started read of %x", this); //mp_fileHandle->WaitForIO(); //Dbg_Message("Done waiting for %x", this); // Should be the callback return true; //PostLoad(assertOnFail, file_size); } else { int file_size = 0; if ( use_pip ) { mp_fileBuffer = Pip::Load( p_fileName ); file_size = Pip::GetFileSize( p_fileName ); Dbg_MsgAssert(file_size, ("Anim file size is 0")); m_pipped = true; } else { // open the file as a stream void* pStream = File::Open( p_fileName,"rb" ); // make sure the file is valid if ( !pStream ) { Dbg_MsgAssert( assertOnFail, ("Load of %s failed - file not found?", p_fileName) ); return false; } file_size = File::GetFileSize(pStream); Dbg_MsgAssert(file_size, ("Anim file size is 0")); Dbg_Assert(!mp_fileBuffer); // Now that we're pipping it, we don't have to force this on the top-down heap // Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); mp_fileBuffer = (void *) Mem::Malloc( file_size ); // Mem::Manager::sHandle().PopContext(); // read the file in if ( !File::Read( mp_fileBuffer, 1, file_size, pStream ) ) { Mem::Free(mp_fileBuffer); mp_fileBuffer = NULL; File::Close( pStream ); Dbg_MsgAssert( assertOnFail, ("Load of %s failed - bad format?", p_fileName) ); return false; } File::Close(pStream); } return PostLoad(assertOnFail, file_size); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CBonedAnimFrameData::async_callback(File::CAsyncFileHandle *, File::EAsyncFunctionType function, int result, unsigned int arg0, unsigned int arg1) { //Dbg_Message("Got callback from %x", arg0); if (function == File::FUNC_READ) { CBonedAnimFrameData *p_data = (CBonedAnimFrameData *) arg0; bool assert = (bool) arg1; p_data->PostLoad(assert, result); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::PostLoad(bool assertOnFail, int file_size, bool delete_buffer) { bool success = false; //Dbg_Message("PostLoad of %x", this); // Handle end of async, if that was used if (mp_fileHandle) { #if 0 if (mp_fileHandle->WaitForIO() != file_size) { Mem::Free(mp_fileBuffer); mp_fileBuffer = NULL; File::CAsyncFileLoader::sClose( mp_fileHandle ); mp_fileHandle = NULL; Dbg_MsgAssert( assertOnFail, ("PostLoad of anim file failed - bad format?") ); return false; } #endif File::CAsyncFileLoader::sClose( mp_fileHandle ); mp_fileHandle = NULL; } uint8 *pFileData = (uint8 *) mp_fileBuffer; // read in header information SBonedAnimFileHeader* pTheHeader = (SBonedAnimFileHeader*)pFileData; pFileData += sizeof(SBonedAnimFileHeader); // store header information m_duration = pTheHeader->duration; m_flags = pTheHeader->flags; // trying to phase out the old partial anim format here... Dbg_MsgAssert( ( m_flags & nxBONEDANIMFLAGS_OLDPARTIALANIM ) == 0, ( "No longer supporting old partial anim format" ) ); // the anim converter should automatically do this, // so that we don't have to do it at runtime... Dbg_MsgAssert( pTheHeader->flags & nxBONEDANIMFLAGS_COMPRESSEDTIME, ( "Expected the anim times to be in frames" ) ); Dbg_MsgAssert( pTheHeader->flags & nxBONEDANIMFLAGS_PREROTATEDROOT, ( "Expected the root bones to be prerotated" ) ); // some debugging information // Dbg_Message( "Loading animation %s", p_fileName ); // Dbg_Message( "Duration = %f", m_duration ); // Dbg_Message( "Flags = %08x", theHeader.flags ); // read the stream differently based on whether it's compressed... if ( pTheHeader->flags & nxBONEDANIMFLAGS_PLATFORM ) { Dbg_Assert( (pTheHeader->flags & nxBONEDANIMFLAGS_USECOMPRESSTABLE) == 0 ); success = this->plat_read_stream( pFileData, delete_buffer ); } else if ( pTheHeader->flags & nxBONEDANIMFLAGS_USECOMPRESSTABLE ) { Dbg_Assert( (pTheHeader->flags & nxBONEDANIMFLAGS_PLATFORM) == 0 ); success = this->plat_read_compressed_stream( pFileData, delete_buffer ); } else { Dbg_MsgAssert( 0, ("Unrecognized file format (flags = %08x)...", pTheHeader->flags) ); } // handle failure Dbg_MsgAssert( success || !assertOnFail, ("PostLoad of anim file failed - bad format?") ); if ( !m_pipped ) { // Done with temp buffer (since it's pipped, don't delete the file buffer) // Mem::Free(mp_fileBuffer); // mp_fileBuffer = NULL; } m_dataLoaded = success; return success; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::IsValidTime( float time ) { return ( time >= 0.0f && time < m_duration ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float get_alpha( float timeStamp1, float timeStamp2, float time ) { Dbg_MsgAssert(timeStamp1 <= time && timeStamp2 >= time, ( "%f should be within [%f %f]", time, timeStamp1, timeStamp2 )); return (( time - timeStamp1 ) / ( timeStamp2 - timeStamp1 )); } // THE FOLLOWING 2 FUNCTIONS SHOULD BE REFACTORED? /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::GetInterpolatedCameraFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* pQuickAnim ) { Dbg_Assert( pRotations ); Dbg_Assert( pTranslations ); Dbg_Assert( is_hires() ); float qAlpha = 0.0f; float tAlpha = 0.0f; // DMA the animation data here. #ifdef __ARAM__ int size; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { size = ( m_numBones * sizeof(uint32) ); } else { size = 0; } if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { size += ( m_numBones * sizeof(SHiResAnimFramePointers) ); } else { size += ( m_numBones * sizeof(SStandardAnimFramePointers) ); } dma_count( (uint32)mp_perBoneFrames, size ); while ( framecount_active ); // if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) // { // dma_partial( (uint32)mp_partialAnimData, ( m_numBones * sizeof( uint16 ) * 2 ) ); // } // while ( framecount_active ); // NsDMA::toMRAM( qqq, (uint32)mp_qFrames, m_num_qFrames * sizeof( CHiResAnimQKey ) ); // NsDMA::toMRAM( ttt, (uint32)mp_tFrames, m_num_tFrames * sizeof( CHiResAnimTKey ) ); // while( framecount_active ); #endif // __ARAM__ // find the nearest 2 keyframes float qTimeStamp = time * 60.0f; float tTimeStamp = time * 60.0f; CHiResAnimQKey* pStartQFrame = NULL; CHiResAnimQKey* pEndQFrame = NULL; CHiResAnimTKey* pStartTFrame = NULL; CHiResAnimTKey* pEndTFrame = NULL; CHiResAnimQKey* pCurrentQFrame = NULL; CHiResAnimTKey* pCurrentTFrame = NULL; #ifdef __ARAM__ // pStartQFrame = p_hqqq; // pEndQFrame = p_hqqq; // // pStartTFrame = p_httt; // pEndTFrame = p_httt; // // pCurrentQFrame = p_hqqq; // pCurrentTFrame = p_httt; uint32 anim_q_offset = (uint32)mp_qFrames; uint32 anim_t_offset = (uint32)mp_tFrames; void * p_bone_frames; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { p_bone_frames = &framecount[m_numBones*2]; } else { p_bone_frames = framecount; } #else pStartQFrame = (CHiResAnimQKey*)mp_qFrames; pEndQFrame = pStartQFrame; pCurrentQFrame = pStartQFrame; pStartTFrame = (CHiResAnimTKey*)mp_tFrames; pEndTFrame = pStartTFrame; pCurrentTFrame = pStartTFrame; void * p_bone_frames = mp_perBoneFrames; #endif // __aram__ for ( int i = 0; i < m_numBones; i++ ) { int numQKeys = get_num_qkeys( p_bone_frames, i ); int numTKeys = get_num_tkeys( p_bone_frames, i ); #ifdef __ARAM__ int q_off = 0; { // DMA this q track. int aligned_off = ( anim_q_offset & ~31 ); int size = ( ( ( sizeof( CHiResAnimQKey ) * numQKeys ) - ( aligned_off - anim_q_offset ) ) + 31 ) & ~31; DCFlushRange ( qqq, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. (u32)aligned_off, // Source. (uint32)qqq, // Dest. size, // Length. arqCallback ); // Callback while ( framecount_active ); q_off = anim_q_offset - aligned_off; pStartQFrame = (CHiResAnimQKey*)&qqq[q_off]; pEndQFrame = (CHiResAnimQKey*)&qqq[q_off]; pCurrentQFrame = (CHiResAnimQKey*)&qqq[q_off]; } #endif // __ARAM__ for ( int j = 0; j < numQKeys; j++ ) { if ( j == (numQKeys-1) ) { // last frame pStartQFrame = pCurrentQFrame + j; pEndQFrame = pCurrentQFrame + j; qAlpha = 0.0f; break; } else if ( qTimeStamp >= timeDown(pCurrentQFrame[j].timestamp) && qTimeStamp <= timeDown(pCurrentQFrame[j+1].timestamp) ) { pStartQFrame = pCurrentQFrame + j; pEndQFrame = pCurrentQFrame + j + 1; qAlpha = get_alpha( timeDown(pStartQFrame->timestamp), timeDown(pEndQFrame->timestamp), qTimeStamp ); break; } } interpolate_q_frame( pRotations, pStartQFrame, pEndQFrame, qAlpha, is_hires() ); #ifdef __ARAM__ int t_off = 0; { // DMA this t track. int aligned_off = ( anim_t_offset & ~31 ); int size = ( ( ( sizeof( CHiResAnimTKey ) * numTKeys ) - ( aligned_off - anim_t_offset ) ) + 31 ) & ~31; DCFlushRange ( qqq, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. (u32)aligned_off, // Source. (uint32)qqq, // Dest. size, // Length. arqCallback ); // Callback while ( framecount_active ); t_off = anim_t_offset - aligned_off; pStartTFrame = (CHiResAnimTKey*)&qqq[t_off]; pEndTFrame = (CHiResAnimTKey*)&qqq[t_off]; pCurrentTFrame = (CHiResAnimTKey*)&qqq[t_off]; } #endif // __ARAM__ for ( int j = 0; j < numTKeys; j++ ) { if ( j == (numTKeys-1) ) { // last frame pStartTFrame = pCurrentTFrame + j; pEndTFrame = pCurrentTFrame + j; tAlpha = 0.0f; break; } else if ( tTimeStamp >= timeDown(pCurrentTFrame[j].timestamp) && tTimeStamp <= timeDown(pCurrentTFrame[j+1].timestamp) ) { pStartTFrame = pCurrentTFrame + j; pEndTFrame = pCurrentTFrame + j + 1; tAlpha = get_alpha( timeDown(pStartTFrame->timestamp), timeDown(pEndTFrame->timestamp), tTimeStamp ); break; } } // theStartFrame and theEndFrame should contain the // two closest keyframes here. now interpolate between them // TODO: we might be able to cache some of this data... interpolate_t_frame( pTranslations, pStartTFrame, pEndTFrame, tAlpha, is_hires() ); #if 0 if ( m_printDebugInfo ) { // for debug info, if necessary } #endif #ifdef __ARAM__ anim_q_offset += ( sizeof( CHiResAnimQKey ) * numQKeys ); anim_t_offset += ( sizeof( CHiResAnimTKey ) * numTKeys ); #else pCurrentQFrame += numQKeys; pCurrentTFrame += numTKeys; #endif // __ARAM__ pRotations++; pTranslations++; } return true; } #ifdef __ARAM__ inline int get_compressed_q_frame( char* pData, CStandardAnimQKey* pReturnData ) #else inline char * get_compressed_q_frame( char* pData, CStandardAnimQKey* pReturnData ) #endif // __ARAM__ { unsigned char* p_data = (unsigned char*)pData; short data = (short)(( p_data[1]<<8 ) | p_data[0] ); pReturnData->timestamp = data & 0x3fff; pReturnData->signBit = data & 0x8000 ? 1 : 0; // skip the timestamp p_data += sizeof( short ); if ( data & 0x4000 ) { if ( !(data & 0x3800) ) { unsigned char d = *p_data; // a char* defaults to a signed char*, // which means you'll try // to access outside the array // boundaries... brutal! //Dbg_Assert( *p_data == d ); CBonedAnimCompressEntry* pEntry = &sQTable[d]; pReturnData->qx = pEntry->x48; pReturnData->qy = pEntry->y48; pReturnData->qz = pEntry->z48; p_data += sizeof( char ); // remove the bits pReturnData->timestamp &= 0x07ff; } else { if ( data & 0x2000 ) { pReturnData->qx = p_data[0]; p_data += sizeof( char ); } else { pReturnData->qx = (p_data[1]<<8) | p_data[0]; p_data += sizeof( short ); } if ( data & 0x1000 ) { pReturnData->qy = p_data[0]; p_data += sizeof( char ); } else { pReturnData->qy = (p_data[1]<<8) | p_data[0]; p_data += sizeof( short ); } if ( data & 0x0800 ) { pReturnData->qz = p_data[0]; p_data += sizeof( char ); } else { pReturnData->qz = (p_data[1]<<8) | p_data[0]; p_data += sizeof( short ); } // remove the bits pReturnData->timestamp &= 0x07ff; } } else { // no compression pReturnData->qx = (p_data[1]<<8) | p_data[0]; pReturnData->qy = (p_data[3]<<8) | p_data[2]; pReturnData->qz = (p_data[5]<<8) | p_data[4]; p_data += 6; } #ifdef __ARAM__ return (int)p_data - (int)pData; #else return (char*)p_data; #endif // __ARAM__ } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __ARAM__ inline int get_compressed_t_frame( char* pData, CStandardAnimTKey* pReturnData ) #else inline char * get_compressed_t_frame( char* pData, CStandardAnimTKey* pReturnData ) #endif // __ARAM__ { unsigned char* p_data = (unsigned char*)pData; bool useLookupTable = false; unsigned char flags = *p_data; p_data += sizeof( char ); if ( flags & 0x80 ) { useLookupTable = true; } if ( flags & 0x40 ) { // expand the timestamp pReturnData->timestamp = (unsigned short)(flags & 0x3f); } else { // read another short pReturnData->timestamp = (unsigned short)(p_data[1]<<8) | p_data[0]; p_data += sizeof( short ); } if ( useLookupTable ) { unsigned char d = *p_data; // defaults to signed char, // which means you'll try // to access outside the array // boundaries... brutal! //Dbg_Assert( *p_data == d ); CBonedAnimCompressEntry* pEntry = &sTTable[d]; pReturnData->tx = pEntry->x48; pReturnData->ty = pEntry->y48; pReturnData->tz = pEntry->z48; p_data += sizeof( char ); } else { // no compression pReturnData->tx = (p_data[1]<<8) | p_data[0]; pReturnData->ty = (p_data[3]<<8) | p_data[2]; pReturnData->tz = (p_data[5]<<8) | p_data[4]; p_data += 6; } #ifdef __ARAM__ return (int)p_data - (int)pData; #else return (char*)p_data; #endif // __ARAM__ } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::GetCompressedInterpolatedFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* pQuickAnim ) { Dbg_Assert( pRotations ); Dbg_Assert( pTranslations ); Dbg_Assert( !is_hires() ); CStandardAnimQKey theStartQFrame; CStandardAnimQKey theEndQFrame; CStandardAnimQKey* pStartQFrame = &theStartQFrame; CStandardAnimQKey* pEndQFrame = &theEndQFrame; CStandardAnimTKey theStartTFrame; CStandardAnimTKey theEndTFrame; CStandardAnimTKey* pStartTFrame = &theStartTFrame; CStandardAnimTKey* pEndTFrame = &theEndTFrame; #ifdef __ARAM__ char* pCurrentQFrame = mp_qFrames;//&mp_qFrames[32]; char* pCurrentTFrame = mp_tFrames;//&mp_tFrames[32]; // Pre-dma framecount sizes. dma_count( (uint32)mp_perBoneQFrameSize, ( m_numBones * sizeof( uint16 ) * 2 ) + 16 ); // +16 should cover partial mask data. // if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) // { // while ( framecount_active ); // dma_partial( (uint32)mp_partialAnimData ); // } // uint32 * partial = (uint32 *)&framecount[( (uint32)mp_partialAnimData - (uint32)mp_perBoneQFrameSize ) * 2]; uint32 * partial = (uint32 *)&framecount[( (uint32)mp_partialAnimData - (uint32)mp_perBoneQFrameSize ) / 2]; // Pre-DMA all data. // uint32 q_lines = ((uint32)mp_boneNames) >> 16; // uint32 t_lines = ((uint32)mp_boneNames) & 0xffff; // q_lines = ( ( q_lines + ( ARAM_CACHE_LINE_SIZE - 1 ) ) / ARAM_CACHE_LINE_SIZE ); // t_lines = ( ( t_lines + ( ARAM_CACHE_LINE_SIZE - 1 ) ) / ARAM_CACHE_LINE_SIZE ); // for ( uint32 lp = 0; lp < q_lines; lp++ ) dma_q_entry( ((uint32)mp_qFrames), lp ); // for ( uint32 lp = 0; lp < t_lines; lp++ ) dma_t_entry( ((uint32)mp_tFrames), lp ); uint16* pQSizes = &framecount[0]; uint16* pTSizes = &framecount[m_numBones]; // Make sure framecount sizes have been DMA'd. while ( framecount_active ); #else Dbg_Assert( mp_qFrames ); Dbg_Assert( mp_tFrames ); char* pCurrentQFrame = (char*)mp_qFrames; char* pCurrentTFrame = (char*)mp_tFrames; uint16* pQSizes = &mp_perBoneQFrameSize[0]; uint16* pTSizes = &mp_perBoneTFrameSize[0]; #endif // __aram__ // find the nearest 2 keyframes float qTimeStamp = time * 60.0f; float tTimeStamp = time * 60.0f; // Precalculate the skip index mask for speed. uint32 skip_index_mask = ( pQuickAnim && pQuickAnim->m_quickAnimPointers.valid ) ? ( 1 << pQuickAnim->m_quickAnimPointers.skipIndex ) : 0; for ( int i = 0; i < m_numBones; i++ ) { // See if the QuickAnim data indicates that this bone may be skipped. bool skip_this_bone = ( skip_index_mask ) ? (( pQuickAnim->m_quickAnimPointers.pSkipList[i] & skip_index_mask ) > 0 ) : false; if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) { #ifdef __ARAM__ uint32* pBoneMask = ( partial + 1 ) + ( i / 32 ); #else uint32* pBoneMask = ( mp_partialAnimData + 1 ) + ( i / 32 ); #endif // __ARAM__ skip_this_bone = ( ( (*pBoneMask) & ( 1 << (i%32) ) ) == 0 ); // skip_this_bone = ( ( (*pBoneMask) & ( (1<<31) >> (i%32) ) ) == 0 ); } if( !skip_this_bone ) { float qAlpha = 0.0f; float tAlpha = 0.0f; char* pNextQFrame = pCurrentQFrame; char* pNextQBone = pCurrentQFrame + *pQSizes; char* pNextTFrame = pCurrentTFrame; char* pNextTBone = pCurrentTFrame + *pTSizes; if ( pQuickAnim && pQuickAnim->m_quickAnimPointers.valid ) { pNextQFrame = pQuickAnim->m_quickAnimPointers.pQuickQKey[i]; pNextTFrame = pQuickAnim->m_quickAnimPointers.pQuickTKey[i]; } #ifdef __ARAM__ int q_off = 0; { // DMA this q track. uint aligned_off = ( (int)pNextQFrame & ~31 ); uint size = ( ( *pQSizes - ( aligned_off - (int)pCurrentQFrame ) ) + 31 ) & ~31; DCFlushRange ( qqq, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. (u32)aligned_off, // Source. (uint32)qqq, // Dest. size, // Length. arqCallback ); // Callback q_off = (int)pNextQFrame - aligned_off; } #endif // __ARAM__ while ( 1 ) { if ( pQuickAnim ) { pQuickAnim->m_quickAnimPointers.pQuickQKey[i] = (char*)pNextQFrame; } # ifdef __ARAM__ while ( framecount_active ); # endif // __ARAM__ #ifdef __ARAM__ int bytes = get_compressed_q_frame( &qqq[q_off], pStartQFrame ); q_off += bytes; pNextQFrame = &pNextQFrame[bytes]; #else pNextQFrame = get_compressed_q_frame( pNextQFrame, pStartQFrame ); #endif // __ARAM__ if ( pNextQFrame >= pNextQBone ) { // last frame *pEndQFrame = *pStartQFrame; qAlpha = 0.0f; break; } #ifdef __ARAM__ get_compressed_q_frame( &qqq[q_off], pEndQFrame ); #else get_compressed_q_frame( pNextQFrame, pEndQFrame ); #endif // __ARAM__ if ( qTimeStamp >= timeDown(pStartQFrame->timestamp) && qTimeStamp <= timeDown(pEndQFrame->timestamp) ) { qAlpha = get_alpha( timeDown(pStartQFrame->timestamp), timeDown(pEndQFrame->timestamp), qTimeStamp ); break; } } // theStartFrame and theEndFrame should contain the // two closest keyframes here. now interpolate between them // TODO: we might be able to cache some of this data... interpolate_standard_q_frame( pRotations, pStartQFrame, pEndQFrame, qAlpha ); #ifdef __ARAM__ int t_off = 0; { // DMA this q track. int aligned_off = ( (int)pNextTFrame & ~31 ); int size = ( ( *pTSizes - ( aligned_off - (int)pCurrentTFrame ) ) + 31 ) & ~31; DCFlushRange ( qqq, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. (u32)aligned_off, // Source. (uint32)qqq, // Dest. size, // Length. arqCallback ); // Callback t_off = (int)pNextTFrame - aligned_off; } #endif // __ARAM__ while ( 1 ) { if ( pQuickAnim ) { pQuickAnim->m_quickAnimPointers.pQuickTKey[i] = (char*)pNextTFrame; } # ifdef __ARAM__ while ( framecount_active ); # endif // __ARAM__ #ifdef __ARAM__ int bytes = get_compressed_t_frame( &qqq[t_off], pStartTFrame ); t_off += bytes; pNextTFrame = &pNextTFrame[bytes]; #else pNextTFrame = get_compressed_t_frame( pNextTFrame, pStartTFrame ); #endif // __ARAM__ if ( pNextTFrame >= pNextTBone ) { // last frame *pEndTFrame = *pStartTFrame; tAlpha = 0.0f; break; } #ifdef __ARAM__ get_compressed_t_frame( &qqq[t_off], pEndTFrame ); #else get_compressed_t_frame( pNextTFrame, pEndTFrame ); #endif // __ARAM__ if ( tTimeStamp >= timeDown(pStartTFrame->timestamp) && tTimeStamp <= timeDown(pEndTFrame->timestamp) ) { tAlpha = get_alpha( timeDown(pStartTFrame->timestamp), timeDown(pEndTFrame->timestamp), tTimeStamp ); break; } } interpolate_standard_t_frame( pTranslations, pStartTFrame, pEndTFrame, tAlpha ); } pCurrentQFrame += *pQSizes; pCurrentTFrame += *pTSizes; pQSizes++; pTSizes++; pRotations++; pTranslations++; } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::GetInterpolatedFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* pQuickAnim ) { if ( m_flags & nxBONEDANIMFLAGS_USECOMPRESSTABLE ) { // new partial anims now use the standard GetCompressedInterpolatedFrames function return GetCompressedInterpolatedFrames(pRotations, pTranslations, time, pQuickAnim); } if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) { Dbg_MsgAssert( 0, ( "Non-compressed partial animations are not supported yet" ) ); } Dbg_Assert( pRotations ); Dbg_Assert( pTranslations ); Dbg_Assert( !is_hires() ); float qAlpha = 0.0f; float tAlpha = 0.0f; // DMA the animation data here. #ifdef __ARAM__ int size; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { size = ( m_numBones * sizeof(uint32) ); } else { size = 0; } if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS ) { size += ( m_numBones * sizeof(SHiResAnimFramePointers) ); } else { size += ( m_numBones * sizeof(SStandardAnimFramePointers) ); } dma_count( (uint32)mp_perBoneFrames, size ); while ( framecount_active ); // if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM ) // { // dma_partial( (uint32)mp_partialAnimData, ( m_numBones * sizeof( uint16 ) * 2 ) ); // } // while ( framecount_active ); // NsDMA::toMRAM( p_sqqq, (uint32)mp_qFrames, m_num_qFrames * sizeof( CStandardAnimQKey ) ); // NsDMA::toMRAM( p_sttt, (uint32)mp_tFrames, m_num_tFrames * sizeof( CStandardAnimTKey ) ); // while( framecount_active ); #endif // __ARAM__ // find the nearest 2 keyframes float qTimeStamp = time * 60.0f; float tTimeStamp = time * 60.0f; CStandardAnimQKey* pStartQFrame = NULL; CStandardAnimQKey* pEndQFrame = NULL; CStandardAnimTKey* pStartTFrame = NULL; CStandardAnimTKey* pEndTFrame = NULL; CStandardAnimQKey* pCurrentQFrame = NULL; CStandardAnimTKey* pCurrentTFrame = NULL; #ifdef __ARAM__ // pStartQFrame = p_sqqq; // pEndQFrame = p_sqqq; // // pStartTFrame = p_sttt; // pEndTFrame = p_sttt; // // pCurrentQFrame = p_sqqq; // pCurrentTFrame = p_sttt; uint32 anim_q_offset = (uint32)mp_qFrames; uint32 anim_t_offset = (uint32)mp_tFrames; void * p_bone_frames; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { p_bone_frames = &framecount[m_numBones*2]; } else { p_bone_frames = framecount; } #else pStartQFrame = (CStandardAnimQKey*)mp_qFrames; pEndQFrame = (CStandardAnimQKey*)mp_qFrames; pStartTFrame = (CStandardAnimTKey*)mp_tFrames; pEndTFrame = (CStandardAnimTKey*)mp_tFrames; pCurrentQFrame = (CStandardAnimQKey*)mp_qFrames; pCurrentTFrame = (CStandardAnimTKey*)mp_tFrames; void * p_bone_frames = mp_perBoneFrames; #endif // __aram__ for ( int i = 0; i < m_numBones; i++ ) { int numQKeys = get_num_qkeys( p_bone_frames, i ); int numTKeys = get_num_tkeys( p_bone_frames, i ); #ifdef __ARAM__ int q_off = 0; { // DMA this q track. int aligned_off = ( anim_q_offset & ~31 ); int size = ( ( ( sizeof( CStandardAnimQKey ) * numQKeys ) - ( aligned_off - anim_q_offset ) ) + 31 ) & ~31; DCFlushRange ( qqq, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. (u32)aligned_off, // Source. (uint32)qqq, // Dest. size, // Length. arqCallback ); // Callback while ( framecount_active ); q_off = anim_q_offset - aligned_off; pStartQFrame = (CStandardAnimQKey*)&qqq[q_off]; pEndQFrame = (CStandardAnimQKey*)&qqq[q_off]; pCurrentQFrame = (CStandardAnimQKey*)&qqq[q_off]; } #endif // __ARAM__ for ( int j = 0; j < numQKeys; j++ ) { if ( j == (numQKeys-1) ) { // last frame pStartQFrame = pCurrentQFrame + j; pEndQFrame = pCurrentQFrame + j; qAlpha = 0.0f; break; } else if ( qTimeStamp >= timeDown(pCurrentQFrame[j].timestamp) && qTimeStamp <= timeDown(pCurrentQFrame[j+1].timestamp) ) { pStartQFrame = pCurrentQFrame + j; pEndQFrame = pCurrentQFrame + j + 1; qAlpha = get_alpha( timeDown(pStartQFrame->timestamp), timeDown(pEndQFrame->timestamp), qTimeStamp ); break; } } interpolate_q_frame( pRotations, pStartQFrame, pEndQFrame, qAlpha, is_hires() ); #ifdef __ARAM__ int t_off = 0; { // DMA this t track. int aligned_off = ( anim_t_offset & ~31 ); int size = ( ( ( sizeof( CStandardAnimTKey ) * numTKeys ) - ( aligned_off - anim_t_offset ) ) + 31 ) & ~31; DCFlushRange ( qqq, size ); framecount_size = size; framecount_active = 1; ARQPostRequest ( &framecount_request, 0x55555555, // Owner. ARQ_TYPE_ARAM_TO_MRAM, // Type. ARQ_PRIORITY_HIGH, // Priority. (u32)aligned_off, // Source. (uint32)qqq, // Dest. size, // Length. arqCallback ); // Callback while ( framecount_active ); t_off = anim_t_offset - aligned_off; pStartTFrame = (CStandardAnimTKey*)&qqq[t_off]; pEndTFrame = (CStandardAnimTKey*)&qqq[t_off]; pCurrentTFrame = (CStandardAnimTKey*)&qqq[t_off]; } #endif // __ARAM__ for ( int j = 0; j < numTKeys; j++ ) { if ( j == (numTKeys-1) ) { // last frame pStartTFrame = pCurrentTFrame + j; pEndTFrame = pCurrentTFrame + j; tAlpha = 0.0f; break; } else if ( tTimeStamp >= timeDown(pCurrentTFrame[j].timestamp) && tTimeStamp <= timeDown(pCurrentTFrame[j+1].timestamp) ) { pStartTFrame = pCurrentTFrame + j; pEndTFrame = pCurrentTFrame + j + 1; tAlpha = get_alpha( timeDown(pStartTFrame->timestamp), timeDown(pEndTFrame->timestamp), tTimeStamp ); break; } } // theStartFrame and theEndFrame should contain the // two closest keyframes here. now interpolate between them // TODO: we might be able to cache some of this data... interpolate_t_frame( pTranslations, pStartTFrame, pEndTFrame, tAlpha, is_hires() ); #if 0 if ( m_printDebugInfo ) { // for debug info, if necessary } #endif #ifdef __ARAM__ anim_q_offset += ( sizeof( CStandardAnimQKey ) * numQKeys ); anim_t_offset += ( sizeof( CStandardAnimTKey ) * numTKeys ); #else pCurrentQFrame += numQKeys; pCurrentTFrame += numTKeys; #endif // __ARAM__ pRotations++; pTranslations++; } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint32 CBonedAnimFrameData::GetBoneName( int index ) { // only the object anim stores the name of the bones Dbg_MsgAssert( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA, ( "Bone names are only stored with object anims" ) ); Dbg_MsgAssert( index >= 0 && index < m_numBones, ( "Bone name request out of range %d (0-%d)", index, m_numBones ) ); Dbg_Assert( mp_boneNames ); #ifdef __ARAM__ int size; if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA ) { size = ( m_numBones * sizeof(uint32) ); } else { size = 0; } dma_count( (uint32)mp_perBoneFrames, size ); while ( framecount_active ); uint32 * p32 = (uint32*)framecount; return p32[index]; #else return mp_boneNames[index]; #endif // __ARAM__ } /******************************************************************/ /* */ /* */ /******************************************************************/ int CBonedAnimFrameData::get_num_customkeys() { return m_num_customKeys; } /******************************************************************/ /* */ /* */ /******************************************************************/ CCustomAnimKey* CBonedAnimFrameData::get_custom_key( int index ) { Dbg_Assert( index >= 0 && index < get_num_customkeys() ); Dbg_MsgAssert( mpp_customAnimKeyList, ( "custom key list doesn't exist" ) ); return mpp_customAnimKeyList[index]; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::ResetCustomKeys() { // this assumes that there's only going to be one // animcontroller calling this frame data... // eventually, we might need to move this // into CReferencedFrameData... int customKeyCount = get_num_customkeys(); for ( int i = 0; i < customKeyCount; i++ ) { CCustomAnimKey* pKey = get_custom_key(i); pKey->SetActive( true ); } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CBonedAnimFrameData::ProcessCustomKeys( float startTime, float endTime, Obj::CObject* pObject, bool inclusive ) { // for each key, // see if it's between the start and end time // start_time <= time < end_time // get it into frames... startTime *= 60.0f; endTime *= 60.0f; int customKeyCount = get_num_customkeys(); for ( int i = 0; i < customKeyCount; i++ ) { CCustomAnimKey* pKey = get_custom_key(i); if ( pKey->WithinRange( startTime, endTime, inclusive ) ) { // printf( "Processing key at %f (%f %f)\n", custom_key_time, startTime, endTime ); pKey->ProcessKey( pObject ); } else { // printf( "Not processing key at %f (%f %f)\n", custom_key_time, startTime, endTime ); } } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Gfx