//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ // //===========================================================================// #include "tier0/dbg.h" #include "mathlib/mathlib.h" #include "bone_setup_PS3.h" #if !defined(__SPU__) #include #include "tier0/vprof.h" #include "bone_accessor.h" #endif #include "mathlib/ssequaternion.h" #include "bone_utils_PS3.h" //----------------------------------------------------------------------------- // Purpose: return a sub frame rotation for a single bone //----------------------------------------------------------------------------- void ExtractAnimValue_PS3( int frame, mstudioanimvalue_t_PS3 *pEA_animvalue, float scale, float &v1, float &v2 ) { if( !pEA_animvalue ) { v1 = v2 = 0; return; } byte ls_buf[2 * 0x10] ALIGN16; // 32bytes to ensure alignment on lower 4 bits of address and crossover of 16B boundary since we may fetch 2 at a time byte ls_buf_v1[2 * 0x10] ALIGN16; byte ls_buf_v2[2 * 0x10] ALIGN16; mstudioanimvalue_t_PS3 *pLS_animvalue; mstudioanimvalue_t_PS3 *pLS_animvalue_v1; mstudioanimvalue_t_PS3 *pLS_animvalue_v2; pLS_animvalue = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_animvalue, sizeof( mstudioanimvalue_t_PS3 ) * 2 ); // * 2 to fetch [1] below // Avoids a crash reading off the end of the data // There is probably a better long-term solution; Ken is going to look into it. if( ( pLS_animvalue->num.total == 1 ) && ( pLS_animvalue->num.valid == 1 ) ) { v1 = v2 = pLS_animvalue[1].value * scale; return; } int k = frame; // find the data list that has the frame while( pLS_animvalue->num.total <= k ) { k -= pLS_animvalue->num.total; // k -= panimvalue->num.total; pEA_animvalue += pLS_animvalue->num.valid + 1; //panimvalue += panimvalue->num.valid + 1; pLS_animvalue = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_animvalue, sizeof( mstudioanimvalue_t_PS3 ) ); // * 2 to fetch [1] below if( pLS_animvalue->num.total == 0 ) { Assert( 0 ); // running off the end of the animation stream is bad v1 = v2 = 0; return; } } if( pLS_animvalue->num.valid > k ) { // has valid animation data //v1 = panimvalue[k+1].value * scale; pLS_animvalue_v1 = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf_v1, (uint32)(pEA_animvalue + k + 1), sizeof( mstudioanimvalue_t_PS3 ) * 2 ); // * 2 to fetch k+2 below v1 = pLS_animvalue_v1->value * scale; if( pLS_animvalue->num.valid > k + 1 ) { // has valid animation blend data //v2 = panimvalue[k+2].value * scale; pLS_animvalue_v2 = pLS_animvalue_v1 + 1; // already loaded above v2 = pLS_animvalue_v2->value * scale; } else { if( pLS_animvalue->num.total > k + 1 ) { // data repeats, no blend v2 = v1; } else { pLS_animvalue_v2 = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf_v2, (uint32)(pEA_animvalue + pLS_animvalue->num.valid + 2), sizeof( mstudioanimvalue_t_PS3 ) ); // pull blend from first data block in next list //v2 = panimvalue[panimvalue->num.valid+2].value * scale; v2 = pLS_animvalue_v2->value * scale; } } } else { // get last valid data block pLS_animvalue_v1 = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf_v1, (uint32)(pEA_animvalue + pLS_animvalue->num.valid), sizeof( mstudioanimvalue_t_PS3 ) ); v1 = pLS_animvalue_v1->value * scale; //v1 = panimvalue[panimvalue->num.valid].value * scale; if( pLS_animvalue->num.total > k + 1 ) { // data repeats, no blend v2 = v1; } else { pLS_animvalue_v2 = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf_v1, (uint32)(pEA_animvalue + pLS_animvalue->num.valid + 2), sizeof( mstudioanimvalue_t_PS3 ) ); // pull blend from first data block in next list v2 = pLS_animvalue_v2->value * scale; // v2 = panimvalue[panimvalue->num.valid + 2].value * scale; } } } void ExtractAnimValue_PS3( int frame, mstudioanimvalue_t_PS3 *pEA_animvalue, float scale, float &v1 ) { if( !pEA_animvalue ) { v1 = 0; return; } byte ls_buf[2 * 0x10] ALIGN16; byte ls_buf_v1[2 * 0x10] ALIGN16; mstudioanimvalue_t_PS3 *pLS_animvalue; mstudioanimvalue_t_PS3 *pLS_animvalue_v1; pLS_animvalue = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_animvalue, sizeof( mstudioanimvalue_t_PS3 ) ); int k = frame; while( pLS_animvalue->num.total <= k ) { k -= pLS_animvalue->num.total; pEA_animvalue += pLS_animvalue->num.valid + 1; //panimvalue += panimvalue->num.valid + 1; pLS_animvalue = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_animvalue, sizeof( mstudioanimvalue_t_PS3 ) ); if( pLS_animvalue->num.total == 0 ) { Assert( 0 ); // running off the end of the animation stream is bad v1 = 0; return; } } if( pLS_animvalue->num.valid > k ) { pLS_animvalue_v1 = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf_v1, (uint32)(pEA_animvalue + k + 1), sizeof( mstudioanimvalue_t_PS3 ) ); v1 = pLS_animvalue_v1->value * scale; // v1 = panimvalue[k+1].value * scale; } else { pLS_animvalue_v1 = (mstudioanimvalue_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf_v1, (uint32)(pEA_animvalue + pLS_animvalue->num.valid), sizeof( mstudioanimvalue_t_PS3 ) ); // get last valid data block v1 = pLS_animvalue_v1->value * scale; // v1 = panimvalue[panimvalue->num.valid].value * scale; } } //----------------------------------------------------------------------------- // Purpose: return a sub frame rotation for a single bone //----------------------------------------------------------------------------- void CalcBoneQuaternion_PS3( int frame, float s, const Quaternion &baseQuat, const RadianEuler &baseRot, const Vector &baseRotScale, int iBaseFlags, const Quaternion &baseAlignment, const mstudio_rle_anim_t_PS3 *pLS_anim, mstudio_rle_anim_t_PS3 *pEA_anim, BoneQuaternion &q ) { byte ls_buf[ 2 * 0x10 ] ALIGN16; // max of Quat48, Quat64, mstudioanim_valueptr_t_PS3 if( pLS_anim->flags & STUDIO_ANIM_RAWROT ) { byte *pEA_quat48 = (byte *)pLS_anim->pQuat48( pEA_anim ); Quaternion48 *pQuat48 = (Quaternion48 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_quat48, sizeof( Quaternion48 ) ); // byte ls_Q48[sizeof(Quaternion48)] ALIGN16; // memcpy(ls_Q48, pQuat48, sizeof(Quaternion48)); // q = *((Quaternion48 *)&ls_Q48); q = *(pQuat48); AssertFatal( q.IsValid() ); return; } if( pLS_anim->flags & STUDIO_ANIM_RAWROT2 ) { byte *pEA_quat64 = (byte *)pLS_anim->pQuat64( pEA_anim ); Quaternion64 *pQuat64 = (Quaternion64 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_quat64, sizeof( Quaternion64 ) ); // byte ls_Q64[sizeof(Quaternion64)] ALIGN16; // memcpy(ls_Q64, pQuat64, sizeof(Quaternion64)); // q = *((Quaternion64 *)&ls_Q64); q = *(pQuat64); // q = *(panim->pQuat64()); AssertFatal( q.IsValid() ); return; } if( !(pLS_anim->flags & STUDIO_ANIM_ANIMROT) ) { if( pLS_anim->flags & STUDIO_ANIM_DELTA ) { q.Init( 0.0f, 0.0f, 0.0f, 1.0f ); } else { q = baseQuat; } return; } mstudioanim_valueptr_t_PS3 *pLS_ValuesPtr; mstudioanim_valueptr_t_PS3 *pEA_ValuesPtr = pLS_anim->pRotV( pEA_anim ); pLS_ValuesPtr = (mstudioanim_valueptr_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_ValuesPtr, sizeof( mstudioanim_valueptr_t_PS3 ) ); if( s > 0.001f ) { BoneQuaternion q1, q2; RadianEuler angle1 ALIGN16; RadianEuler angle2 ALIGN16; ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 0 ), baseRotScale.x, angle1.x, angle2.x ); ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 1 ), baseRotScale.y, angle1.y, angle2.y ); ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 2 ), baseRotScale.z, angle1.z, angle2.z ); if( !(pLS_anim->flags & STUDIO_ANIM_DELTA) ) { fltx4 a1, a2, br1; a1 = LoadAlignedSIMD( angle1.Base() ); a2 = LoadAlignedSIMD( angle2.Base() ); br1 = LoadUnalignedSIMD( baseRot.Base() ); a1 = AddSIMD( a1, br1 ); a2 = AddSIMD( a2, br1 ); StoreUnaligned3SIMD( angle1.Base(), a1 ); StoreUnaligned3SIMD( angle2.Base(), a2 ); // angle1.x = angle1.x + baseRot.x; // angle1.y = angle1.y + baseRot.y; // angle1.z = angle1.z + baseRot.z; // // angle2.x = angle2.x + baseRot.x; // angle2.y = angle2.y + baseRot.y; // angle2.z = angle2.z + baseRot.z; } AssertFatal( angle1.IsValid() && angle2.IsValid() ); if( angle1.x != angle2.x || angle1.y != angle2.y || angle1.z != angle2.z ) { AngleQuaternion_PS3( angle1, q1 ); AngleQuaternion_PS3( angle2, q2 ); #ifdef _X360 fltx4 q1simd, q2simd, qsimd; q1simd = LoadAlignedSIMD( q1 ); q2simd = LoadAlignedSIMD( q2 ); qsimd = QuaternionBlendSIMD( q1simd, q2simd, s ); StoreUnalignedSIMD( q.Base(), qsimd ); #else QuaternionBlend_PS3( q1, q2, s, q ); // QuaternionBlend( q1, q2, s, q ); #endif } else { AngleQuaternion_PS3( angle1, q ); } } else { RadianEuler angle ALIGN16; ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 0 ), baseRotScale.x, angle.x ); ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 1 ), baseRotScale.y, angle.y ); ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 2 ), baseRotScale.z, angle.z ); if( !(pLS_anim->flags & STUDIO_ANIM_DELTA) ) { fltx4 a1, br1; a1 = LoadAlignedSIMD( angle.Base() ); br1 = LoadUnalignedSIMD( baseRot.Base() ); a1 = AddSIMD( a1, br1 ); StoreUnaligned3SIMD( angle.Base(), a1 ); // angle.x = angle.x + baseRot.x; // angle.y = angle.y + baseRot.y; // angle.z = angle.z + baseRot.z; } AssertFatal( angle.IsValid() ); AngleQuaternion_PS3( angle, q ); } AssertFatal( q.IsValid() ); // align to unified bone if( !(pLS_anim->flags & STUDIO_ANIM_DELTA) && (iBaseFlags & BONE_FIXED_ALIGNMENT) ) { QuaternionAlign_PS3( baseAlignment, q, q ); } } // inline void CalcBoneQuaternion_PS3( int frame, float s, // const mstudiobone_t *pBone, // const mstudiolinearbone_t *pLinearBones, // const mstudio_rle_anim_t *panim, BoneQuaternion &q ) // { // if( pLinearBones ) // { // CalcBoneQuaternion_PS3( frame, s, pLinearBones->quat(panim->bone), pLinearBones->rot(panim->bone), pLinearBones->rotscale(panim->bone), pLinearBones->flags(panim->bone), pLinearBones->qalignment(panim->bone), panim, q ); // } // else // { // CalcBoneQuaternion_PS3( frame, s, pBone->quat, pBone->rot, pBone->rotscale, pBone->flags, pBone->qAlignment, panim, q ); // } // } //----------------------------------------------------------------------------- // Purpose: return a sub frame position for a single bone //----------------------------------------------------------------------------- void CalcBonePosition_PS3( int frame, float s, const Vector &basePos, const Vector &baseBoneScale, const mstudio_rle_anim_t_PS3 *pLS_anim, mstudio_rle_anim_t_PS3 *pEA_anim, BoneVector &pos ) { byte ls_buf[ 2 * 0x10 ] ALIGN16; // max Vec48, studioanim_valueptr_t_PS3 if( pLS_anim->flags & STUDIO_ANIM_RAWPOS ) { byte *pEA_pos = (byte *)pLS_anim->pPos( pEA_anim ); Vector48 *pPos = (Vector48 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_pos, sizeof( Vector48 ) ); // byte ls_P48[sizeof(Vector48)] ALIGN16; // memcpy(ls_P48, pPos, sizeof(Vector48)); // pos = *((Vector48 *)&ls_P48); pos = *(pPos); AssertFatal( pos.IsValid() ); return; } else if( !(pLS_anim->flags & STUDIO_ANIM_ANIMPOS) ) { if( pLS_anim->flags & STUDIO_ANIM_DELTA ) { pos.Init( 0.0f, 0.0f, 0.0f ); } else { pos = basePos; } return; } mstudioanim_valueptr_t_PS3 *pLS_ValuesPtr; mstudioanim_valueptr_t_PS3 *pEA_ValuesPtr = pLS_anim->pPosV( pEA_anim ); pLS_ValuesPtr = (mstudioanim_valueptr_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_ValuesPtr, sizeof( mstudioanim_valueptr_t_PS3 ) ); int j; if( s > 0.001f ) { // float v1, v2; // for( j = 0; j < 3; j++ ) // { // ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, j ), baseBoneScale[j], v1, v2 ); // pos[j] = v1 * (1.0f - s) + v2 * s; // } VectorAligned p1, p2; ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 0 ), baseBoneScale[0], p1[0], p2[0] ); ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 1 ), baseBoneScale[1], p1[1], p2[1] ); ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, 2 ), baseBoneScale[2], p1[2], p2[2] ); fltx4 vp1, vp2; fltx4 s1, pos1; vp1 = LoadAlignedSIMD( &p1 ); vp2 = LoadAlignedSIMD( &p2 ); s1 = ReplicateX4( s ); pos1 = MsubSIMD( vp1, s1, vp1 ); pos1 = MaddSIMD( vp2, s1, pos1 ); StoreAlignedSIMD( pos.Base(), pos1 ); } else { for( j = 0; j < 3; j++ ) { ExtractAnimValue_PS3( frame, pLS_ValuesPtr->pAnimvalue( pEA_ValuesPtr, j ), baseBoneScale[j], pos[j] ); } } if( !(pLS_anim->flags & STUDIO_ANIM_DELTA) ) { fltx4 bp1, p1; p1 = LoadAlignedSIMD( &pos ); bp1 = LoadUnalignedSIMD( &basePos ); p1 = AddSIMD( p1, bp1 ); StoreAlignedSIMD( pos.Base(), p1 ); // pos.x = pos.x + basePos.x; // pos.y = pos.y + basePos.y; // pos.z = pos.z + basePos.z; } AssertFatal( pos.IsValid() ); } // inline void CalcBonePosition_PS3( int frame, float s, // const mstudiobone_t *pBone, // const mstudiolinearbone_t *pEA_LinearBones, // const mstudio_rle_anim_t *panim, BoneVector &pos ) // { // if( pEA_LinearBones ) // { // CalcBonePosition_PS3( frame, s, pLinearBones->pos(panim->bone), pLinearBones->posscale(panim->bone), panim, pos ); // } // else // { // CalcBonePosition_PS3( frame, s, pBone->pos, pBone->posscale, panim, pos ); // } // } inline void CalcBonePositionQuaternion_PS3( int frame, float s, mstudiobone_t_PS3 *pEA_bone, const bonejob_SPU *pBonejob, mstudiolinearbone_t_PS3 *pEA_linearBones, mstudio_rle_anim_t_PS3 *pLS_anim, mstudio_rle_anim_t_PS3 *pEA_anim, BoneVector &pos, BoneQuaternion &q ) { byte boneData[ 16 * 0x10 ] ALIGN16; if( pBonejob->pEA_studiohdr_linearBones ) { int *pLS_flags; Vector *pLS_pos; Vector *pLS_posscale; Vector *pLS_rotscale; Quaternion *pLS_quat; Quaternion *pLS_qalignment; RadianEuler *pLS_rot; // pos pLS_pos = (Vector *)SPUmemcpy_UnalignedGet_MustSync( boneData, (uint32)((Vector *)pBonejob->pEA_linearbones_pos + pLS_anim->bone), sizeof(Vector), DMATAG_ANIM ); // posscale pLS_posscale = (Vector *)SPUmemcpy_UnalignedGet_MustSync( boneData + (2*0x10), (uint32)((Vector *)pBonejob->pEA_linearbones_posscale + pLS_anim->bone), sizeof(Vector), DMATAG_ANIM ); SPUmemcpy_Sync( 1<pEA_linearbones_rotscale + pLS_anim->bone), sizeof(Vector), DMATAG_ANIM ); // quat pLS_quat = (Quaternion *)SPUmemcpy_UnalignedGet_MustSync( boneData + (6*0x10), (uint32)((Quaternion *)pBonejob->pEA_linearbones_quat + pLS_anim->bone), sizeof(Quaternion), DMATAG_ANIM ); // quat pLS_qalignment = (Quaternion *)SPUmemcpy_UnalignedGet_MustSync( boneData + (8*0x10), (uint32)((Quaternion *)pBonejob->pEA_linearbones_qalignment + pLS_anim->bone), sizeof(Quaternion), DMATAG_ANIM ); // rot pLS_rot = (RadianEuler *)SPUmemcpy_UnalignedGet_MustSync( boneData + (10*0x10), (uint32)((RadianEuler *)pBonejob->pEA_linearbones_rot + pLS_anim->bone), sizeof(RadianEuler), DMATAG_ANIM ); // flags pLS_flags = (int *)SPUmemcpy_UnalignedGet_MustSync( boneData + (12*0x10), (uint32)((int *)pBonejob->pEA_linearbones_flags + pLS_anim->bone), sizeof(int), DMATAG_ANIM ); SPUmemcpy_Sync( 1<pos, pLS_bonedata->posscale, pLS_anim, pEA_anim, pos ); CalcBoneQuaternion_PS3( frame, s, pLS_bonedata->quat, pLS_bonedata->rot, pLS_bonedata->rotscale, pLS_bonedata->flags, pLS_bonedata->qAlignment, pLS_anim, pEA_anim, q ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CalcDecompressedAnimation_PS3( void *pEA_Compressed, const mstudiocompressedikerror_t_PS3 *pLS_Compressed, int iFrame, float fraq, BoneVector &pos, BoneQuaternion &q ) { if( fraq > 0.0001f ) { VectorAligned p1; VectorAligned p2; ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 0 ), pLS_Compressed->scale[0], p1.x, p2.x ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 1 ), pLS_Compressed->scale[1], p1.y, p2.y ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 2 ), pLS_Compressed->scale[2], p1.z, p2.z ); // pos = p1 * (1.0f - fraq) + p2 * fraq; fltx4 vp1, vp2; fltx4 f1, pos1; vp1 = LoadAlignedSIMD( &p1 ); vp2 = LoadAlignedSIMD( &p2 ); f1 = ReplicateX4( fraq ); pos1 = MsubSIMD( vp1, f1, vp1 ); pos1 = MaddSIMD( vp2, f1, pos1 ); StoreAlignedSIMD( pos.Base(), pos1 ); BoneQuaternion q1, q2; RadianEuler angle1, angle2; ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 3 ), pLS_Compressed->scale[3], angle1.x, angle2.x ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 4 ), pLS_Compressed->scale[4], angle1.y, angle2.y ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 5 ), pLS_Compressed->scale[5], angle1.z, angle2.z ); if( angle1.x != angle2.x || angle1.y != angle2.y || angle1.z != angle2.z ) { AngleQuaternion_PS3( angle1, q1 ); AngleQuaternion_PS3( angle2, q2 ); QuaternionBlend_PS3( q1, q2, fraq, q ); // QuaternionBlend( q1, q2, fraq, q ); } else { AngleQuaternion_PS3( angle1, q ); } } else { ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 0 ), pLS_Compressed->scale[0], pos.x ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 1 ), pLS_Compressed->scale[1], pos.y ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 2 ), pLS_Compressed->scale[2], pos.z ); RadianEuler angle; ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 3 ), pLS_Compressed->scale[3], angle.x ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 4 ), pLS_Compressed->scale[4], angle.y ); ExtractAnimValue_PS3( iFrame, pLS_Compressed->pAnimvalue( pEA_Compressed, 5 ), pLS_Compressed->scale[5], angle.z ); AngleQuaternion_PS3( angle, q ); } AssertFatal( pos.IsValid() ); AssertFatal( q.IsValid() ); } #if 0 // not supported on SPU yet, rare path //----------------------------------------------------------------------------- // Purpose: translate animations done in a non-standard parent space //----------------------------------------------------------------------------- static void CalcLocalHierarchyAnimation_PS3( const bonejob_SPU *pBonejob, const CStudioHdr *pStudioHdr, matrix3x4a_t *boneToWorld, CBoneBitList_PS3 &boneComputed, BoneVector *pos, BoneQuaternion *q, //const mstudioanimdesc_t &animdesc, const mstudiobone_t *pbone, mstudiolocalhierarchy_t *pHierarchy, int iBone, int iNewParent, float cycle, int iFrame, float flFraq, int boneMask ) { BoneVector localPos; BoneQuaternion localQ; // make fake root transform static matrix3x4a_t rootXform; SetIdentityMatrix( rootXform ); // FIXME: missing check to see if seq has a weight for this bone float weight = 1.0f; // check to see if there's a ramp on the influence if ( pHierarchy->tail - pHierarchy->peak < 1.0f ) { float index = cycle; if (pHierarchy->end > 1.0f && index < pHierarchy->start) index += 1.0f; if (index < pHierarchy->start) return; if (index >= pHierarchy->end) return; if (index < pHierarchy->peak && pHierarchy->start != pHierarchy->peak) { weight = (index - pHierarchy->start) / (pHierarchy->peak - pHierarchy->start); } else if (index > pHierarchy->tail && pHierarchy->end != pHierarchy->tail) { weight = (pHierarchy->end - index) / (pHierarchy->end - pHierarchy->tail); } weight = SimpleSpline( weight ); } CalcDecompressedAnimation_PS3( pHierarchy->pLocalAnim(), iFrame - pHierarchy->iStart, flFraq, localPos, localQ ); // find first common root bone int iRoot1 = iBone; int iRoot2 = iNewParent; while (iRoot1 != iRoot2 && iRoot1 != -1) { if (iRoot1 > iRoot2) iRoot1 = pStudioHdr->boneParent( iRoot1 ); else iRoot2 = pStudioHdr->boneParent( iRoot2 ); } // BUGBUG: pos and q only valid if local weight BuildBoneChainPartial_PS3( pBonejob, rootXform, pos, q, iBone, boneToWorld, boneComputed, iRoot1 ); BuildBoneChainPartial_PS3( pBonejob, rootXform, pos, q, iNewParent, boneToWorld, boneComputed, iRoot1 ); matrix3x4a_t localXform; AngleMatrix_PS3( localQ, localPos, localXform ); ConcatTransforms_Aligned_PS3( boneToWorld[iNewParent], localXform, boneToWorld[iBone] ); // back solve BoneVector p1; BoneQuaternion q1; int n = pbone[iBone].parent; if (n == -1) { if (weight == 1.0f) { MatrixAngles_PS3( boneToWorld[iBone], q[iBone], pos[iBone] ); } else { MatrixAngles_PS3( boneToWorld[iBone], q1, p1 ); QuaternionSlerp_PS3( q[iBone], q1, weight, q[iBone] ); pos[iBone] = Lerp_PS3( weight, p1, pos[iBone] ); } } else { matrix3x4a_t worldToBone; MatrixInvert_PS3( boneToWorld[n], worldToBone ); matrix3x4a_t local; ConcatTransforms_Aligned_PS3( worldToBone, boneToWorld[iBone], local ); if (weight == 1.0f) { MatrixAngles_PS3( local, q[iBone], pos[iBone] ); } else { MatrixAngles_PS3( local, q1, p1 ); QuaternionSlerp_PS3( q[iBone], q1, weight, q[iBone] ); pos[iBone] = Lerp_PS3( weight, p1, pos[iBone] ); } } } #endif //----------------------------------------------------------------------------- // Purpose: Calc Zeroframe Data //----------------------------------------------------------------------------- static void CalcZeroframeData_PS3( const animData_SPU *pAnim, const mstudioanimdesc_t_PS3 *pLS_animdesc, const bonejob_SPU *pBonejob, int *animgroup_masterBone, int *animbone_flags, float fFrame, BoneVector *pos, BoneQuaternion *q, int boneMask, float flWeight ) { void *pEA_animdesc = pAnim->pEA_animdesc; byte *pEA_Data = pLS_animdesc->pZeroFrameData( pEA_animdesc );//byte *)pAnim->pEA_animdesc_pZeroFrameData; byte *pLS_Data; if( !pEA_Data ) return; int i, j; byte ls_buf[ 0x10 * 4 ] ALIGN16; // Vector48 * 3 and overlap if( pLS_animdesc->zeroframecount == 1 ) { // get pAnimbone flags // get masterbone for( j = 0; j < pAnim->animstudiohdr_numbones; j++ ) { if( animgroup_masterBone ) { i = animgroup_masterBone[ j ]; } else { i = j; } if( animbone_flags[ j ] & BONE_HAS_SAVEFRAME_POS ) { if( (i >= 0) && (pBonejob->boneFlags[ i ] & boneMask) ) { // TODO: simple data fetch for now, optimize when working pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_Data, sizeof( Vector48 ) ); Vector p = *(Vector48 *)pLS_Data; //pos[i] = pos[i] * (1.0f - flWeight) + p * flWeight; fltx4 posi, w1, p1; posi = LoadAlignedSIMD( &pos[i] ); p1 = LoadUnalignedSIMD( &p ); w1 = ReplicateX4( flWeight ); posi = MsubSIMD( posi, w1, posi ); posi = MaddSIMD( p1, w1, posi ); StoreAlignedSIMD( pos[i].Base(), posi ); AssertFatal( pos[i].IsValid() ); } pEA_Data += sizeof( Vector48 ); } if( animbone_flags[ j ] & BONE_HAS_SAVEFRAME_ROT64 ) { if( (i >= 0) && (pBonejob->boneFlags[ i ] & boneMask) ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_Data, sizeof( Quaternion64 ) ); Quaternion q0 = *(Quaternion64 *)pLS_Data; QuaternionBlend_PS3( q[i], q0, flWeight, q[i] ); // QuaternionBlend( q[i], q0, flWeight, q[i] ); AssertFatal( q[i].IsValid() ); } pEA_Data += sizeof( Quaternion64 ); } else if( animbone_flags[ j ] & BONE_HAS_SAVEFRAME_ROT32 ) { if( (i >= 0) && (pBonejob->boneFlags[ i ] & boneMask) ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_Data, sizeof( Quaternion32 ) ); Quaternion q0 = *(Quaternion32 *)pLS_Data; QuaternionBlend_PS3( q[i], q0, flWeight, q[i] ); // QuaternionBlend( q[i], q0, flWeight, q[i] ); AssertFatal( q[i].IsValid() ); } pEA_Data += sizeof( Quaternion32 ); } } } else { float s1; int index = (int)(fFrame / (float)pLS_animdesc->zeroframespan); if( index >= pLS_animdesc->zeroframecount - 1 ) { index = pLS_animdesc->zeroframecount - 2; s1 = 1.0f; } else { s1 = clamp( (fFrame - index * pLS_animdesc->zeroframespan) / pLS_animdesc->zeroframespan, 0.0f, 1.0f ); } int i0 = MAX( index - 1, 0 ); int i1 = index; int i2 = MIN( index + 1, pLS_animdesc->zeroframecount - 1 ); int off1 = (i1 - i0); int off2 = (i2 - i0); for( j = 0; j < pAnim->animstudiohdr_numbones; j++ ) { if( animgroup_masterBone ) { i = animgroup_masterBone[ j ]; } else { i = j; } if( animbone_flags[ j ] & BONE_HAS_SAVEFRAME_POS ) { if( (i >= 0) && (pBonejob->boneFlags[ i ] & boneMask) ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)(((Vector48 *)pEA_Data) + i0), sizeof( Vector48 ) * 3 ); Vector p0 = *(((Vector48 *)pLS_Data)); // pEA_Data + i0 Vector p1 = *(((Vector48 *)pLS_Data) + off1); // pEA_Data + i1 Vector p2 = *(((Vector48 *)pLS_Data) + off2); // pEA_Data + i2 if( flWeight == 1.0f ) { // don't blend into an uninitialized value Hermite_Spline_PS3( p0, p1, p2, s1, pos[i] ); } else { Vector p3; Hermite_Spline_PS3( p0, p1, p2, s1, p3 ); pos[i] = pos[i] * (1.0f - flWeight) + p3 * flWeight; } AssertFatal( pos[i].IsValid() ); } pEA_Data += sizeof( Vector48 ) * pLS_animdesc->zeroframecount; } if( animbone_flags[ j ] & BONE_HAS_SAVEFRAME_ROT64 ) { if( (i >= 0) && (pBonejob->boneFlags[ i ] & boneMask) ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)(((Quaternion64 *)pEA_Data) + i0), sizeof( Quaternion64 ) * 3 ); Quaternion q0 = *(((Quaternion64 *)pLS_Data)); // pEA_Data + i0 Quaternion q1 = *(((Quaternion64 *)pLS_Data) + off1); // pEA_Data + i1 Quaternion q2 = *(((Quaternion64 *)pLS_Data) + off2); // pEA_Data + i2 if( flWeight == 1.0f ) { // don't blend into an uninitialized value Hermite_Spline_PS3( q0, q1, q2, s1, q[i] ); } else { Quaternion q3; Hermite_Spline_PS3( q0, q1, q2, s1, q3 ); QuaternionBlend_PS3( q[i], q3, flWeight, q[i] ); // QuaternionBlend( q[i], q3, flWeight, q[i] ); } AssertFatal( q[i].IsValid() ); } pEA_Data += sizeof( Quaternion64 ) * pLS_animdesc->zeroframecount; } else if( animbone_flags[ j ] & BONE_HAS_SAVEFRAME_ROT32 ) { if( (i >= 0) && (pBonejob->boneFlags[ i ] & boneMask) ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)(((Quaternion32 *)pEA_Data) + i0), sizeof( Quaternion32 ) * 3 ); Quaternion q0 = *(((Quaternion32 *)pLS_Data)); // pEA_Data + i0 Quaternion q1 = *(((Quaternion32 *)pLS_Data) + off1); // pEA_Data + i1 Quaternion q2 = *(((Quaternion32 *)pLS_Data) + off2); // pEA_Data + i2 if (flWeight == 1.0f) { // don't blend into an uninitialized value Hermite_Spline_PS3( q0, q1, q2, s1, q[i] ); } else { Quaternion q3; Hermite_Spline_PS3( q0, q1, q2, s1, q3 ); QuaternionBlend_PS3( q[i], q3, flWeight, q[i] ); // QuaternionBlend( q[i], q3, flWeight, q[i] ); } AssertFatal( q[i].IsValid() ); } pEA_Data += sizeof( Quaternion32 ) * pLS_animdesc->zeroframecount; } } } } //----------------------------------------------------------------------------- // Purpose: Extract and blend two frames from a mstudio_frame_anim_t block of data //----------------------------------------------------------------------------- inline byte *ExtractTwoFrames_PS3( byte flags, float s, byte *pEA_FrameData, byte *&pEA_ConstantData, int framelength, BoneQuaternion &q, BoneVector &pos, bool bIsDelta = false, const mstudiolinearbone_t_PS3 *pEA_LinearBones = NULL, int bone = 0 ) { byte ls_buf1[ 0x10 * 2 ] ALIGN16; byte ls_buf2[ 0x10 * 2 ] ALIGN16; byte *pLS_Data1; byte *pLS_Data2; if( flags & STUDIO_FRAME_ANIM_ROT ) { pLS_Data2 = (byte *)SPUmemcpy_UnalignedGet_MustSync( ls_buf2, (uint32)(pEA_FrameData + framelength), sizeof( Quaternion48 ), DMATAG_ANIM ); pLS_Data1 = (byte *)SPUmemcpy_UnalignedGet( ls_buf1, (uint32)pEA_FrameData, sizeof( Quaternion48 ) ); fltx4 q1 = UnpackQuaternion48SIMD( (Quaternion48 *)(pLS_Data1) ); SPUmemcpy_Sync( 1<quat( bone ); } } if( flags & STUDIO_FRAME_ANIM_POS ) { pLS_Data2 = (byte *)SPUmemcpy_UnalignedGet_MustSync( ls_buf2, (uint32)(pEA_FrameData + framelength), sizeof(Vector48), DMATAG_ANIM ); pLS_Data1 = (byte *)SPUmemcpy_UnalignedGet( ls_buf1, (uint32)pEA_FrameData, sizeof(Vector48) ); fltx4 p1 = UnpackVector48SIMD( (Vector48 *)(pLS_Data1) ); SPUmemcpy_Sync( 1<pos( bone ); } } return pEA_FrameData; } //----------------------------------------------------------------------------- // Purpose: Extract one frame from a mstudio_frame_anim_t block of data //----------------------------------------------------------------------------- inline byte *ExtractSingleFrame_PS3( byte flags, byte *pEA_FrameData, byte *&pEA_ConstantData, BoneQuaternion &q, BoneVector &pos, bool bIsDelta = false, const mstudiolinearbone_t_PS3 *pEA_LinearBones = NULL, int bone = 0 ) { byte ls_buf[ 0x10 * 2 ] ALIGN16; byte *pLS_Data; if( flags & STUDIO_FRAME_ANIM_ROT ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_FrameData, sizeof( Quaternion48 ) ); fltx4 flt = UnpackQuaternion48SIMD( (Quaternion48 *)(pLS_Data) ); StoreAlignedSIMD( (QuaternionAligned*)&q, flt ); AssertFatal( q.IsValid() ); pEA_FrameData += sizeof( Quaternion48 ); } else if( flags & STUDIO_FRAME_ANIM_ROT2 ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_FrameData, sizeof( Quaternion48S ) ); //ALIGN StoreUnalignedSIMD( q.Base(), (fltx4) *((Quaternion48S *)(pLS_Data)) ); StoreAlignedSIMD( q.Base(), (fltx4) *((Quaternion48S *)(pLS_Data)) ); AssertFatal( q.IsValid() ); pEA_FrameData += sizeof( Quaternion48S ); } else if( flags & STUDIO_FRAME_CONST_ROT ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_ConstantData, sizeof( Quaternion48 ) ); fltx4 flt = UnpackQuaternion48SIMD( (Quaternion48 *)(pLS_Data) ); StoreAlignedSIMD( (QuaternionAligned*)&q, flt ); AssertFatal( q.IsValid() ); pEA_ConstantData += sizeof( Quaternion48 ); } else if( flags & STUDIO_FRAME_CONST_ROT2 ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_ConstantData, sizeof( Quaternion48S ) ); //ALIGN StoreUnalignedSIMD( q.Base(), (fltx4) *((Quaternion48S *)(pLS_Data)) ); StoreAlignedSIMD( q.Base(), (fltx4) *((Quaternion48S *)(pLS_Data)) ); AssertFatal( q.IsValid() ); pEA_ConstantData += sizeof( Quaternion48S ); } // the non-virtual version needs initializers for no-animation else if( pEA_LinearBones ) { if( bIsDelta ) { q.Init( 0.0f, 0.0f, 0.0f, 1.0f ); } else { // take from ::init results q = g_qInit[ bone ]; //q = pLinearBones->quat( bone ); } } if( flags & STUDIO_FRAME_ANIM_POS ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_FrameData, sizeof( Vector48 ) ); fltx4 flt = UnpackVector48SIMD( (Vector48 *)(pLS_Data) ); //ALIGN StoreUnaligned3SIMD( pos.Base(), flt ); StoreAlignedSIMD( pos.Base(), flt ); AssertFatal( pos.IsValid() ); pEA_FrameData += sizeof( Vector48 ); } else if( flags & STUDIO_FRAME_CONST_POS ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_ConstantData, sizeof( Vector48 ) ); fltx4 flt = UnpackVector48SIMD( (Vector48 *)(pLS_Data) ); //ALIGN StoreUnaligned3SIMD( pos.Base(), flt ); StoreAlignedSIMD( pos.Base(), flt ); AssertFatal( pos.IsValid() ); pEA_ConstantData += sizeof( Vector48 ); } else if( flags & STUDIO_FRAME_ANIM_POS2 ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_FrameData, sizeof( Vector ) ); fltx4 flt = LoadUnaligned3SIMD( (float *)(pLS_Data) ); //ALIGN StoreUnaligned3SIMD( pos.Base(), flt ); StoreAlignedSIMD( pos.Base(), flt ); AssertFatal( pos.IsValid() ); pEA_FrameData += sizeof( Vector ); } else if( flags & STUDIO_FRAME_CONST_POS2 ) { pLS_Data = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_ConstantData, sizeof( Vector ) ); fltx4 flt = LoadUnaligned3SIMD( (float *)(pLS_Data) ); //ALIGN StoreUnaligned3SIMD( pos.Base(), flt ); StoreAlignedSIMD( pos.Base(), flt ); AssertFatal( pos.IsValid() ); pEA_ConstantData += sizeof( Vector ); } // the non-virtual version needs initializers for no-animation else if( pEA_LinearBones ) { if (bIsDelta) { pos.Init( 0.0f, 0.0f, 0.0f ); } else { // take from ::init results pos = g_posInit[ bone ]; //pos = pLinearBones->pos( bone ); } } return pEA_FrameData; } //----------------------------------------------------------------------------- // Purpose: Skip forward to the next bone in a mstudio_frame_anim_t block of data //----------------------------------------------------------------------------- inline byte *SkipBoneFrame_PS3( byte flags, byte *pEA_FrameData, byte *&pEA_ConstantData ) { if( flags & STUDIO_FRAME_ANIM_ROT ) { pEA_FrameData += sizeof( Quaternion48 ); } else if( flags & STUDIO_FRAME_ANIM_ROT2 ) { pEA_FrameData += sizeof( Quaternion48S ); } else if( flags & STUDIO_FRAME_CONST_ROT ) { pEA_ConstantData += sizeof( Quaternion48 ); } else if( flags & STUDIO_FRAME_CONST_ROT2 ) { pEA_ConstantData += sizeof( Quaternion48S ); } if( flags & STUDIO_FRAME_ANIM_POS ) { pEA_FrameData += sizeof( Vector48 ); } else if( flags & STUDIO_FRAME_CONST_POS ) { pEA_ConstantData += sizeof( Vector48 ); } else if( flags & STUDIO_FRAME_ANIM_POS2 ) { pEA_FrameData += sizeof( Vector ); } else if( flags & STUDIO_FRAME_CONST_POS2 ) { pEA_ConstantData += sizeof( Vector ); } return pEA_FrameData; } #if 0 // not supported on SPU - no callers //----------------------------------------------------------------------------- // Purpose: Extract a single bone of animation //----------------------------------------------------------------------------- void SetupSingleBoneMatrix_PS3( CStudioHdr *pOwnerHdr, int nSequence, int iFrame, int iBone, matrix3x4a_t &mBoneLocal ) { // FIXME: why does anyone call this instead of just looking up that entities cached animation? // Reading the callers, I don't see how what it returns is of any use mstudioseqdesc_t &seqdesc = pOwnerHdr->pSeqdesc( nSequence ); mstudioanimdesc_t &animdesc = pOwnerHdr->pAnimdesc( seqdesc.anim( 0, 0 ) ); int iLocalFrame = iFrame; float s = 0; const mstudiobone_t *pbone = pOwnerHdr->pBone( iBone ); BoneQuaternion boneQuat; BoneVector bonePos; bool bFound = false; if (animdesc.flags & STUDIO_FRAMEANIM) { /* mstudio_frame_anim_t *pFrameanim = (mstudio_frame_anim_t *)animdesc.pAnim( &iLocalFrame ); if (pFrameanim) { byte *pBoneFlags = pFrameanim->pBoneFlags( ); byte *pConstantData = pFrameanim->pConstantData( ); byte *pFrameData = pFrameanim->pFrameData( iLocalFrame ); // FIXME: this is the local bone index, not the global bone index for (int i = 0; i < iBone; i++, pBoneFlags++) { pFrameData = SkipBoneFrame( *pBoneFlags, pFrameData, pConstantData ); } pFrameData = ExtractSingleFrame( *pBoneFlags, pFrameData, pConstantData, boneQuat, bonePos ); bFound = true; } */ } else { mstudio_rle_anim_t *panim = (mstudio_rle_anim_t *)animdesc.pAnim( &iLocalFrame ); // search for bone // FIXME: this is the local bone index, not the global bone index while (panim && panim->bone != iBone) { panim = panim->pNext(); } // look up animation if found, if not, initialize if (panim && seqdesc.weight(iBone) > 0) { CalcBoneQuaternion_PS3( iLocalFrame, s, pbone, NULL, panim, boneQuat ); CalcBonePosition_PS3 ( iLocalFrame, s, pbone, NULL, panim, bonePos ); bFound = true; } } if (!bFound) { if (animdesc.flags & STUDIO_DELTA) { boneQuat.Init( 0.0f, 0.0f, 0.0f, 1.0f ); bonePos.Init( 0.0f, 0.0f, 0.0f ); } else { boneQuat = pbone->quat; bonePos = pbone->pos; } } QuaternionMatrix( boneQuat, bonePos, mBoneLocal ); } #endif //----------------------------------------------------------------------------- // Purpose: Find and decode a sub-frame of animation, remapping the skeleton bone indexes //----------------------------------------------------------------------------- static void CalcVirtualAnimation_PS3( const bonejob_SPU *pBonejob, const accumposeentry_SPU *pPoseEntry, const animData_SPU *pAnim, BoneVector *pos, BoneQuaternion *q, const int *boneMap, const float *boneWeight, int animIndex, float cycle, int boneMask ) { SNPROF_ANIM("CalcVirtualAnimation_PS3"); int i, j, k; mstudiobone_t_PS3 *pEA_Animbone = (mstudiobone_t_PS3 *)pAnim->pEA_anim_bones_pos; byte ls_frameanim[ sizeof(mstudio_frame_anim_t_PS3)+0x10 ] ALIGN16; int ls_masterBone[ MAXSTUDIOBONES_PS3+4 ] ALIGN16; int *masterBone; // get masterbone[] once masterBone = (int *)SPUmemcpy_UnalignedGet_MustSync( ls_masterBone, (uint32)pAnim->pEA_animgroup_masterbone, sizeof(int) * pAnim->animstudiohdr_numbones, DMATAG_ANIM ); // get animdesc byte ls_animdesc[sizeof(mstudioanimdesc_t_PS3)+16] ALIGN16; mstudioanimdesc_t_PS3 *pLS_animdesc; pLS_animdesc = (mstudioanimdesc_t_PS3 *)SPUmemcpy_UnalignedGet( ls_animdesc, (uint32)pAnim->pEA_animdesc, sizeof(mstudioanimdesc_t_PS3) ); int iFrame; float s; float fFrame = pPoseEntry->cycle * (pLS_animdesc->numframes - 1); iFrame = (int)fFrame; s = (fFrame - iFrame); float flStall = pAnim->flStall; mstudio_rle_anim_t_PS3 *pEA_anim = NULL; mstudio_rle_anim_t_PS3 *pLS_anim = NULL; mstudio_frame_anim_t_PS3 *pEA_Frameanim = NULL; mstudio_frame_anim_t_PS3 *pLS_Frameanim = NULL; byte boneFlags[ MAXSTUDIOBONES_PS3+0x10 ] ALIGN16; byte *pBoneFlags = NULL; byte *pEA_FrameData = NULL; byte *pEA_ConstantData = NULL; if( pLS_animdesc->flags & STUDIO_FRAMEANIM ) { pEA_Frameanim = (mstudio_frame_anim_t_PS3 *)pAnim->pEA_animdesc_pFrameanim; if ( pEA_Frameanim) { pLS_Frameanim = (mstudio_frame_anim_t_PS3 *)SPUmemcpy_UnalignedGet( ls_frameanim, (uint32)pEA_Frameanim, sizeof(mstudio_frame_anim_t_PS3) ); void *pEA_boneflags = pLS_Frameanim->pBoneFlags( pEA_Frameanim ); // prefetch pBoneFlags = (byte *)SPUmemcpy_UnalignedGet_MustSync( boneFlags, (uint32)pEA_boneflags, sizeof(byte) * pAnim->animstudiohdr_numbones, DMATAG_ANIM ); pEA_FrameData = pLS_Frameanim->pFrameData( pEA_Frameanim, pAnim->animdesc_iLocalFrame );//(byte *)pAnim->pEA_animdesc_frameData; pEA_ConstantData = pLS_Frameanim->pConstantData( pEA_Frameanim );//(byte *)pAnim->pEA_animdesc_constantData; } } else { pEA_anim = (mstudio_rle_anim_t_PS3 *)pAnim->pEA_animdesc_panim; } int nBoneList[ MAXSTUDIOBONES_PS3 + 4]; int animboneFlags[ MAXSTUDIOBONES_PS3 + 4 ]; int *pAnimboneFlags = NULL; int nBoneListCount = 0; // bonemap SPUmemcpy_Sync( 1<numBones; i++ ) { if( pBonejob->boneFlags[i] & boneMask ) { int j = boneMap[i]; if( j >= 0 && boneWeight[j] > 0.0f ) { nBoneList[ nBoneListCount++ ] = i; } } } if( pLS_animdesc->flags & STUDIO_DELTA ) { for( i = 0; i < nBoneListCount; i++ ) { int nBone = nBoneList[ i ]; q[nBone].Init( 0.0f, 0.0f, 0.0f, 1.0f ); pos[nBone].Init( 0.0f, 0.0f, 0.0f ); } } else if( pPoseEntry->pEA_seq_linearBones ) { Vector *pLS_pos; Quaternion *pLS_quat; byte ls_pos[ 0x10 * 2 ] ALIGN16; byte ls_q[ 0x10 * 2 ] ALIGN16; // try interleaving dma's with loop for( i = 0; i < nBoneListCount; i++ ) { int nBone = nBoneList[i]; int j = boneMap[nBone]; pLS_pos = (Vector *)SPUmemcpy_UnalignedGet_MustSync( ls_pos, (uint32)((Vector *)pPoseEntry->pEA_seq_linearbones_pos + j), sizeof(Vector), DMATAG_ANIM_SYNC_POSQ ); // const Vector *pLinearPos = &pSeqLinearBones->pos( 0 ); pLS_quat = (Quaternion *)SPUmemcpy_UnalignedGet_MustSync( ls_q, (uint32)((Quaternion *)pPoseEntry->pEA_seq_linearbones_quat + j), sizeof(Quaternion), DMATAG_ANIM_SYNC_POSQ ); // const Quaternion *pLinearQuat = &pSeqLinearBones->quat( 0 ); SPUmemcpy_Sync( 1<pEA_seq_bones_pos + j), sizeof(Vector) + sizeof(Quaternion) ); pLS_bonepos = (Vector *)pLS_posquat; pLS_bonequat = (Quaternion *)(pLS_posquat + sizeof(Vector)); pos[nBone] = *pLS_bonepos; // pSeqbone[j].pos; q[nBone] = *pLS_bonequat; // pSeqbone[j].quat; AssertFatal(pos[nBone].IsValid()); AssertFatal(q[nBone].IsValid()); } } // sync on masterbone DMA SPUmemcpy_Sync( 1<pEA_animdesc_pFrameanim ) { if( s > 0.0f ) { for( i = 0; i < pAnim->animstudiohdr_numbones; i++ ) { j = masterBone[i]; if( j >= 0 && (pBonejob->boneFlags[j] & boneMask) ) { pEA_FrameData = ExtractTwoFrames_PS3( *pBoneFlags, s, pEA_FrameData, pEA_ConstantData, pLS_Frameanim->framelength, q[j], pos[j] ); } else { pEA_FrameData = SkipBoneFrame_PS3( *pBoneFlags, pEA_FrameData, pEA_ConstantData ); } pBoneFlags++; } } else { for( i = 0; i < pAnim->animstudiohdr_numbones; i++ ) { j = masterBone[i]; if( j >= 0 && (pBonejob->boneFlags[j] & boneMask) ) { pEA_FrameData = ExtractSingleFrame_PS3( *pBoneFlags, pEA_FrameData, pEA_ConstantData, q[j], pos[j] ); } else { pEA_FrameData = SkipBoneFrame_PS3( *pBoneFlags, pEA_FrameData, pEA_ConstantData ); } pBoneFlags++; } } } else if( pEA_anim ) { byte ls_buf[ 0x10 * 2 ] ALIGN16; // anim pLS_anim = (mstudio_rle_anim_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_anim, sizeof( mstudio_rle_anim_t_PS3 ) ); // FIXME: change encoding so that bone -1 is never the case while( pEA_anim && pLS_anim->bone < 255 ) { j = masterBone[ pLS_anim->bone ]; if( j >= 0 && (pBonejob->boneFlags[j] & boneMask ) ) { k = boneMap[ j ]; if( k >= 0 && boneWeight[ k ] > 0.0f ) { CalcBonePositionQuaternion_PS3( pAnim->animdesc_iLocalFrame, s, pEA_Animbone + pLS_anim->bone, pBonejob, (mstudiolinearbone_t_PS3 *)pAnim->pEA_anim_linearBones, pLS_anim, pEA_anim, pos[j], q[j] ); } } pEA_anim = pLS_anim->pNext( pEA_anim ); if( pEA_anim ) { pLS_anim = (mstudio_rle_anim_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_anim, sizeof( mstudio_rle_anim_t_PS3 ) ); } } } else { // gather flags, lazy looped copy for now // TODO: dma chain, but this is a rare path, still worthwhile? int ls_int[ 8 ] ALIGN16; // 32B int *pLS_int; pAnimboneFlags = animboneFlags; for( int lp = 0; lp < pAnim->animstudiohdr_numbones; lp++ ) { pLS_int = (int *)SPUmemcpy_UnalignedGet( ls_int, (uint32)((mstudiobone_t_PS3 *)pAnim->pEA_anim_bones_flags + lp), sizeof( int ) ); pAnimboneFlags[ lp ] = *pLS_int; } CalcZeroframeData_PS3( pAnim, pLS_animdesc, pBonejob, masterBone, pAnimboneFlags, fFrame, pos, q, boneMask, 1.0f ); return; } // cross fade in previous zeroframe data if( flStall > 0.0f ) { if( !pAnimboneFlags ) { int ls_int[ 8 ] ALIGN16; // 32B int *pLS_int; pAnimboneFlags = animboneFlags; for( int lp = 0; lp < pAnim->animstudiohdr_numbones; lp++ ) { pLS_int = (int *)SPUmemcpy_UnalignedGet( ls_int, (uint32)((mstudiobone_t_PS3 *)pAnim->pEA_anim_bones_flags + lp), sizeof( int ) ); pAnimboneFlags[ lp ] = *pLS_int; } } CalcZeroframeData_PS3( pAnim, pLS_animdesc, pBonejob, masterBone, pAnimboneFlags, fFrame, pos, q, boneMask, 1.0f ); } #if 0 // don't take this path on SPU - rare path, do on PPU only // calculate a local hierarchy override if( animdesc.numlocalhierarchy ) { matrix3x4a_t *boneToWorld = g_matStack[0]; CBoneBitList_PS3 boneComputed; int i; for( i = 0; i < animdesc.numlocalhierarchy; i++ ) { mstudiolocalhierarchy_t *pHierarchy = animdesc.pHierarchy( i ); if( !pHierarchy ) break; int iBone = pAnimGroup->masterBone[pHierarchy->iBone]; if( iBone >= 0 && (pStudioHdr->boneFlags(iBone) & boneMask) ) { int iNewParent = pAnimGroup->masterBone[pHierarchy->iNewParent]; if( iNewParent >= 0 && (pStudioHdr->boneFlags(iNewParent) & boneMask) ) { CalcLocalHierarchyAnimation_PS3( pBonejob, pStudioHdr, boneToWorld, boneComputed, pos, q, pbone, pHierarchy, iBone, iNewParent, cycle, iFrame, s, boneMask ); } } } } #endif return; } //----------------------------------------------------------------------------- // Purpose: Find and decode a sub-frame of animation //----------------------------------------------------------------------------- void CalcAnimation_PS3( const bonejob_SPU *pBonejob, const accumposeentry_SPU *pPoseEntry, BoneVector *pos, BoneQuaternion *q, const int *boneMap, const float *boneWeight, int animIndex, float cycle, int boneMask ) { SNPROF_ANIM( "CalcAnimation_PS3" ); const animData_SPU *pAnim = &pPoseEntry->anims[ animIndex ]; if( pBonejob->pEA_studiohdr_vmodel ) { CalcVirtualAnimation_PS3( pBonejob, pPoseEntry, pAnim, pos, q, boneMap, boneWeight, animIndex, cycle, boneMask ); return; } byte ls_frameanim[ sizeof(mstudio_frame_anim_t_PS3)+0x10 ] ALIGN16; // get animdesc byte ls_animdesc[ sizeof(mstudioanimdesc_t_PS3)+0x10 ] ALIGN16; mstudioanimdesc_t_PS3 *pLS_animdesc; pLS_animdesc = (mstudioanimdesc_t_PS3 *)SPUmemcpy_UnalignedGet( ls_animdesc, (uint32)pAnim->pEA_animdesc, sizeof(mstudioanimdesc_t_PS3) ); int i, iFrame; float s; float fFrame = pPoseEntry->cycle * (pLS_animdesc->numframes - 1); iFrame = (int)fFrame; s = (fFrame - iFrame); float flStall = pAnim->flStall; mstudio_rle_anim_t_PS3 *pEA_anim = NULL; mstudio_rle_anim_t_PS3 *pLS_anim = NULL; mstudio_frame_anim_t_PS3 *pEA_Frameanim = NULL; mstudio_frame_anim_t_PS3 *pLS_Frameanim = NULL; mstudiobone_t_PS3 *pEA_bone = (mstudiobone_t_PS3 *)pBonejob->pEA_studiohdr_bones_pos; // = pEA_studiohdr_bones + studiobone_posoffset, pts to pos member // Vector *pEA_LinearBone_pos = (Vector *)pBonejob->pEA_linearbones_pos; const float *pweight = boneWeight; bool bIsDelta = (pLS_animdesc->flags & STUDIO_DELTA) != 0; byte boneFlags[ MAXSTUDIOBONES_PS3+0x10 ] ALIGN16; byte *pBoneFlags = NULL; byte *pEA_FrameData = NULL; byte *pEA_ConstantData = NULL; if( pLS_animdesc->flags & STUDIO_FRAMEANIM ) { pEA_Frameanim = (mstudio_frame_anim_t_PS3 *)pAnim->pEA_animdesc_pFrameanim; if( pEA_Frameanim ) { pLS_Frameanim = (mstudio_frame_anim_t_PS3 *)SPUmemcpy_UnalignedGet( ls_frameanim, (uint32)pEA_Frameanim, sizeof(mstudio_frame_anim_t_PS3) ); void *pEA_boneflags = pLS_Frameanim->pBoneFlags( pEA_Frameanim ); // prefetch pBoneFlags = (byte *)SPUmemcpy_UnalignedGet_MustSync( boneFlags, (uint32)pEA_boneflags, sizeof(byte) * pBonejob->numBones, DMATAG_ANIM ); pEA_FrameData = pLS_Frameanim->pFrameData( pEA_Frameanim, pAnim->animdesc_iLocalFrame );//(byte *)pAnim->pEA_animdesc_frameData; pEA_ConstantData = pLS_Frameanim->pConstantData( pEA_Frameanim );//(byte *)pAnim->pEA_animdesc_constantData; } } else { pEA_anim = (mstudio_rle_anim_t_PS3 *)pAnim->pEA_animdesc_panim; } SPUmemcpy_Sync( 1<pEA_animdesc_panim && !pAnim->pEA_animdesc_pFrameanim ) { // pre initialize if( bIsDelta ) { for( i = 0; i < pBonejob->numBones; i++, pweight++ ) { if( *pweight > 0.0f && (pBonejob->boneFlags[i] & boneMask) ) { q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); pos[i].Init( 0.0f, 0.0f, 0.0f ); } } } else { // TODO: can we just use ::init results here? // TODO: prefetch - don't worry as this path is rare? // TODO: dma chain? byte ls_posquat[ 0x10 * 3 ] ALIGN16; byte *pLS_posquat; Vector *pLS_bonepos; Quaternion *pLS_bonequat; for( i = 0; i < pBonejob->numBones; i++, pEA_bone++, pweight++ ) { // NOTE: we don't want to pull in every studiobone (212B), so we short-dma just the pos and quat here // could build a dma chain. DMA's assume: // 1. offset is offset from start of mstudiobone_t to pos field. // 2. quat field immeiately follows pos field. pLS_posquat = (byte *)SPUmemcpy_UnalignedGet( ls_posquat, (uint32)pEA_bone, sizeof(Vector) + sizeof(Quaternion) ); pLS_bonepos = (Vector *)pLS_posquat; pLS_bonequat = (Quaternion *)(pLS_posquat + sizeof(Vector)); if( *pweight> 0.0f && (pBonejob->boneFlags[i] & boneMask) ) { pos[i] = *((Vector *)(pLS_bonepos)); q[i] = *((Quaternion *)(pLS_bonequat)); AssertFatal(pos[i].IsValid()); AssertFatal(q[i].IsValid()); } } } //CalcZeroframeData_PS3( pStudioHdr, pStudioHdr->GetRenderHdr(), NULL, pStudioHdr->pBone( 0 ), animdesc, fFrame, pos, q, boneMask, 1.0f ); CalcZeroframeData_PS3( pAnim, pLS_animdesc, pBonejob, NULL, (int *)pBonejob->boneFlags, fFrame, pos, q, boneMask, 1.0f ); return; } // sync on boneflags SPUmemcpy_Sync( 1< 0.0f ) { for( i = 0; i < pBonejob->numBones; i++, pBoneFlags++, pweight++ ) { if( *pweight > 0.0f && (pBonejob->boneFlags[i] & boneMask) ) { pEA_FrameData = ExtractTwoFrames_PS3( *pBoneFlags, s, pEA_FrameData, pEA_ConstantData, pLS_Frameanim->framelength, q[i], pos[i], bIsDelta, (mstudiolinearbone_t_PS3 *)pBonejob->pEA_studiohdr_linearBones, i ); } else { pEA_FrameData = SkipBoneFrame_PS3( *pBoneFlags, pEA_FrameData, pEA_ConstantData ); } } } else { for( i = 0; i < pBonejob->numBones; i++, pBoneFlags++, pweight++ ) { if( *pweight > 0.0f && (pBonejob->boneFlags[i] & boneMask) ) { pEA_FrameData = ExtractSingleFrame_PS3( *pBoneFlags, pEA_FrameData, pEA_ConstantData, q[i], pos[i], bIsDelta, (mstudiolinearbone_t_PS3 *)pBonejob->pEA_studiohdr_linearBones, i ); } else { pEA_FrameData = SkipBoneFrame_PS3( *pBoneFlags, pEA_FrameData, pEA_ConstantData ); } } } } else { byte ls_buf[ 0x10 * 4 ] ALIGN16; // BUGBUG: the sequence, the anim, and the model can have all different bone mappings. for( i = 0; i < pBonejob->numBones; i++, pEA_bone++, pweight++ ) { // get panim if( pEA_anim ) { pLS_anim = (mstudio_rle_anim_t_PS3 *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_anim, sizeof( mstudio_rle_anim_t_PS3 ) ); } if( pEA_anim && pLS_anim->bone == i ) { if( *pweight > 0.0f && (pBonejob->boneFlags[i] & boneMask) ) { CalcBonePositionQuaternion_PS3( pAnim->animdesc_iLocalFrame, s, pEA_bone, pBonejob, (mstudiolinearbone_t_PS3 *)pBonejob->pEA_studiohdr_linearBones, pLS_anim, pEA_anim, pos[i], q[i] ); } pEA_anim = pLS_anim->pNext( pEA_anim ); } else if( *pweight > 0.0f && (pBonejob->boneFlags[i] & boneMask) ) { byte *pLS_posquat; Vector *pLS_bonepos; Quaternion *pLS_bonequat; if( bIsDelta ) { q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); pos[i].Init( 0.0f, 0.0f, 0.0f ); } else { pLS_posquat = (byte *)SPUmemcpy_UnalignedGet( ls_buf, (uint32)pEA_bone, sizeof(Vector) + sizeof(Quaternion) ); pLS_bonepos = (Vector *)pLS_posquat; pLS_bonequat = (Quaternion *)(pLS_posquat + sizeof(Vector)); pos[i] = *((Vector *)(pLS_bonepos)); // pbone->pos q[i] = *((Quaternion *)(pLS_bonequat)); // pbone->quat AssertFatal(pos[i].IsValid()); AssertFatal(q[i].IsValid()); } } } } // cross fade in previous zeroframe data if( flStall > 0.0f ) { //CalcZeroframeData_PS3( pStudioHdr, pStudioHdr->GetRenderHdr(), NULL, pStudioHdr->pBone( 0 ), animdesc, fFrame, pos, q, boneMask, flStall ); CalcZeroframeData_PS3( pAnim, pLS_animdesc, pBonejob, NULL, (int *)pBonejob->boneFlags, fFrame, pos, q, boneMask, 1.0f ); } #if 0 // don't take this path on SPU - rare path, do on PPU only // calculate a local hierarchy override if( animdesc.numlocalhierarchy ) { matrix3x4a_t *boneToWorld = g_matStack[0]; CBoneBitList_PS3 boneComputed; int i; for (i = 0; i < animdesc.numlocalhierarchy; i++) { mstudiolocalhierarchy_t *pHierarchy = animdesc.pHierarchy( i ); if ( !pHierarchy ) break; if (pStudioHdr->boneFlags(pHierarchy->iBone) & boneMask) { if (pStudioHdr->boneFlags(pHierarchy->iNewParent) & boneMask) { CalcLocalHierarchyAnimation_PS3( pBonejob, pStudioHdr, boneToWorld, boneComputed, pos, q, pbone, pHierarchy, pHierarchy->iBone, pHierarchy->iNewParent, cycle, iFrame, s, boneMask ); } } } } #endif return; }