//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ // //===========================================================================// #include "mathlib/mathlib.h" #include "bone_setup_PS3.h" #include "bone_utils_PS3.h" //-------------------------------------------------------------------------------------------------------------------- // // local data for a bone job // //-------------------------------------------------------------------------------------------------------------------- #define CPSPATH_FALSE 0 #define CPSPATH_CALCANIM1 1 #define CPSPATH_CALCANIM1_SCALE1MINUSS0 2 #define CPSPATH_CALCANIM1_SCALES0 3 #define CPSPATH_CALCANIM2_S0 4 #define CPSPATH_CALCANIM2_S1 5 #define CPSPATH_CALCANIM3 6 #define CPSPATH_CALCANIM4 7 ALIGN16 byte g_ls_Stack[ (LS_FLOATSTACK_SIZE * sizeof(float)) + (LS_INTSTACK_SIZE * sizeof(int)) ]; byte *g_ls_StackPtr; byte *g_ls_StackPtr_MAX; #if defined(__SPU__) BoneVector *g_posInit; BoneQuaternion *g_qInit; ikcontextikrule_t_PS3 *g_addDep_IKRules; int g_addDep_numIKRules; #else BoneVector g_posInit[ MAXSTUDIOBONES_PS3 ]; // 2k BoneQuaternion g_qInit[ MAXSTUDIOBONES_PS3 ]; // 2k ikcontextikrule_t_PS3 g_addDep_IKRules[ MAX_IKRULES ] ALIGN16; int g_addDep_numIKRules; #endif //static int g_accumPoseCount; #if !defined(__SPU__) #include #include "tier0/vprof.h" //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- extern IVJobs * g_pVJobs; CBoneJobs g_BoneJobs; CBoneJobs* g_pBoneJobs = &g_BoneJobs; job_accumpose::JobDescriptor_t g_AccumPoseJobDescriptor ALIGN128; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CBoneJobs::Init( void ) { m_bEnabled = false; m_boneJobCount = 0; m_boneJobData.EnsureCapacity( 96 ); // requires a SPURS instance, so register with VJobs if( g_pVJobs ) { g_pVJobs->Register( this ); } } void CBoneJobs::Shutdown() { g_pVJobs->Unregister( this ); } void CBoneJobs::OnVjobsInit() { m_bEnabled = true; g_AccumPoseJobDescriptor.header = *m_pRoot->m_pJobAccumPose; g_AccumPoseJobDescriptor.header.useInOutBuffer = 1; g_AccumPoseJobDescriptor.header.sizeStack = (12*1024)/8; g_AccumPoseJobDescriptor.header.sizeInOrInOut = 0; } void CBoneJobs::OnVjobsShutdown() { m_bEnabled = false; } void CBoneJobs::StartFrame( int maxBoneJobs ) { m_boneJobData.EnsureCapacity( maxBoneJobs ); } void CBoneJobs::EndFrame( void ) { m_boneJobData.RemoveAll(); } void CBoneJobs::ResetBoneJobs( void ) { m_boneJobCount = 0; m_boneJobNextSPURSPort = 0; } int CBoneJobs::AddBoneJob( void ) { return m_boneJobCount++; } int CBoneJobs::GetNumBoneJobs( void ) { return m_boneJobCount; } PS3BoneJobData *CBoneJobs::GetJobData( int job ) { return &m_boneJobData[ job ]; } int CBoneJobs::GetNextFreeSPURSPort( void ) { m_boneJobNextSPURSPort = (m_boneJobNextSPURSPort + 1) % VJobsRoot::MAXPORTS_ANIM; return m_boneJobNextSPURSPort; } //----------------------------------------------------------------------------- // Contains support for running BoneJobs on SPU // // each bonejob contains a number of AccumulatePose calls that were 'discovered' // in the 1st pass on PPU - this pass filled the bonejob packet with data for all // AccumulatePose calls that take place on C_BaseAnimating // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Purpose: calculate a pose for a single sequence //----------------------------------------------------------------------------- void InitPose_PS3( const CStudioHdr *pStudioHdr, BoneVector pos[], BoneQuaternion q[], int boneMask ) { // const fltx4 zeroQ = Four_Origin; SNPROF_ANIM("InitPose_PS3"); if ( mstudiolinearbone_t *pLinearBones = pStudioHdr->pLinearBones() ) { int numBones = pStudioHdr->numbones(); AssertFatal( sizeof(BoneQuaternion) == sizeof(Quaternion) ); memcpy( q, (((byte *)pLinearBones) + pLinearBones->quatindex), sizeof( Quaternion ) * numBones ); // want to align pos entries to 16B // memcpy( pos, (((byte *)pLinearBones) + pLinearBones->posindex), sizeof( Vector ) * numBones ); Vector *pSrcBones = (Vector *)(((byte *)pLinearBones) + pLinearBones->posindex); for( int i = 0; i < pStudioHdr->numbones(); i++ ) { pos[i] = pSrcBones[i]; } } else { for( int i = 0; i < pStudioHdr->numbones(); i++ ) { if( pStudioHdr->boneFlags( i ) & boneMask ) { const mstudiobone_t *pbone = pStudioHdr->pBone( i ); pos[i] = pbone->pos; q[i] = pbone->quat; } /* // unnecessary to initialize unused bones since they are ignored downstream. else { pos[i].Zero(); // q[i] = zeroQ; StoreAlignedSIMD(q[i].Base(), zeroQ); } */ } } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- inline bool PoseIsAllZeros_PS3( const CStudioHdr *pStudioHdr, int sequence, mstudioseqdesc_t &seqdesc, int i0, int i1 ) { int baseanim; // remove "zero" positional blends baseanim = pStudioHdr->iRelativeAnim( sequence, seqdesc.anim(i0 ,i1 ) ); mstudioanimdesc_t &anim = ((CStudioHdr *)pStudioHdr)->pAnimdesc( baseanim ); return (anim.flags & STUDIO_ALLZEROS) != 0; } //----------------------------------------------------------------------------- // fill animdata_SPU struct //----------------------------------------------------------------------------- static void SetAnimData_AnimDesc( animData_SPU *pAnim, accumposeentry_SPU *pPoseEntry, mstudioanimdesc_t &animdesc, int animIndex ) { SNPROF_ANIM("SetAnimData_AnimDesc"); int iFrame; float s; float flStall; pAnim->pEA_animdesc = &animdesc; float fFrame = pPoseEntry->cycle * (animdesc.numframes - 1); iFrame = (int)fFrame; s = (fFrame - iFrame); int iLocalFrame = iFrame; pAnim->pEA_animdesc_pFrameanim = NULL; pAnim->pEA_animdesc_panim = NULL; if( animdesc.flags & STUDIO_FRAMEANIM ) { const mstudio_frame_anim_t *pFrameanim = (mstudio_frame_anim_t *)animdesc.pAnim( &iLocalFrame, flStall ); pAnim->animdesc_iLocalFrame = iLocalFrame; pAnim->pEA_animdesc_pFrameanim = (void *)pFrameanim; } else { pAnim->pEA_animdesc_panim = (void *)animdesc.pAnim( &iLocalFrame, flStall ); pAnim->animdesc_iLocalFrame = iLocalFrame; } pAnim->pEA_animdesc_ikrule = NULL; pAnim->pEA_animdesc_ikrulezeroframe = NULL; mstudioikrule_t *pIKRule = animdesc.pIKRule( 0 ); if( pIKRule ) { pAnim->pEA_animdesc_ikrule = pIKRule; } else { mstudioikrulezeroframe_t *pZeroFrameRule = animdesc.pIKRuleZeroFrame( 0 ); pAnim->pEA_animdesc_ikrulezeroframe = pZeroFrameRule; } pAnim->flStall = flStall; } //----------------------------------------------------------------------------- // Fill PoseEntry anim data //----------------------------------------------------------------------------- bool CBoneSetup_PS3::SetAnimData( accumposeentry_SPU *pPoseEntry, const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int sequence, int x, int y, int animIndex, float weight ) { SNPROF_ANIM("CBoneSetup_PS3::SetAnimData"); animData_SPU *pAnim = &pPoseEntry->anims[ animIndex ]; pAnim->seqdesc_anim = seqdesc.anim( x, y ); pAnim->seqdesc_weight = weight; virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); if( pVModel ) { int baseanimation = pStudioHdr->iRelativeAnim( sequence, pAnim->seqdesc_anim ); mstudioanimdesc_t &animdesc = ((CStudioHdr *)pStudioHdr)->pAnimdesc( baseanimation ); if( animdesc.numlocalhierarchy ) { // don't take SPU path m_errorFlags = BONEJOB_ERROR_LOCALHIER; // TODO: follow this path out quickly return false; } SetAnimData_AnimDesc( pAnim, pPoseEntry, animdesc, animIndex ); pAnim->pEA_animgroup_masterbone = pVModel->pAnimGroup( baseanimation )->masterBone.Base(); const studiohdr_t *pAnimStudioHdr; pAnimStudioHdr = ((CStudioHdr *)pStudioHdr)->pAnimStudioHdr( baseanimation ); pAnim->animstudiohdr_numbones = pAnimStudioHdr->numbones; mstudiolinearbone_t *pLinearBones = pAnimStudioHdr->pLinearBones(); pAnim->pEA_anim_linearBones = pLinearBones; pAnim->pEA_anim_bones_pos = &pAnimStudioHdr->pBone(0)->pos; pAnim->pEA_anim_bones_flags = &pAnimStudioHdr->pBone(0)->flags; } else { mstudioanimdesc_t &animdesc = ((CStudioHdr *)pStudioHdr)->pAnimdesc( pAnim->seqdesc_anim ); if( animdesc.numlocalhierarchy ) { // don't take SPU path m_errorFlags = BONEJOB_ERROR_LOCALHIER; // TODO: follow this path out quickly return false; } SetAnimData_AnimDesc( pAnim, pPoseEntry, animdesc, animIndex ); pAnim->animstudiohdr_numbones = pStudioHdr->GetRenderHdr()->numbones; } return true; } //----------------------------------------------------------------------------- // CalcPoseSingle PPU pass - fill PoseEntry // Get this working first before moving more to SPU //----------------------------------------------------------------------------- bool CBoneSetup_PS3::CalcPoseSingle( accumposeentry_SPU *pPoseEntry, const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int sequence, float cycle, const float poseParameter[], float flTime ) { SNPROF_ANIM( "CBoneSetup_PS3::CalcPoseSingle" ); ConVarRef anim_3wayblend( "anim_3wayblend" ); bool bResult = true; float s0, s1; int i0, i1; s0 = 0.0f; s1 = 0.0f; i0 = Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, sequence, 0, s0 ); i1 = Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, sequence, 1, s1 ); //pPoseEntry->cps = Studio_CPS( pStudioHdr, seqdesc, sequence, poseParameter ); if( seqdesc.flags & STUDIO_REALTIME ) { float cps = Studio_CPS( pStudioHdr, seqdesc, sequence, poseParameter ); cycle = flTime * cps; cycle = cycle - (int)cycle; } else if( seqdesc.flags & STUDIO_CYCLEPOSE ) { int iPose = pStudioHdr->GetSharedPoseParameter( sequence, seqdesc.cycleposeindex ); if( iPose != -1 ) { /* const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iPose ); cycle = poseParameter[ iPose ] * (Pose.end - Pose.start) + Pose.start; */ cycle = poseParameter[ iPose ]; } else { cycle = 0.0f; } } else if( cycle < 0.0f || cycle >= 1.0f ) { if( seqdesc.flags & STUDIO_LOOPING ) { cycle = cycle - (int)cycle; if( cycle < 0 ) cycle += 1.0f; } else { cycle = clamp( cycle, 0.0f, 1.0f ); } } pPoseEntry->cyclePoseSingle = cycle; pPoseEntry->i0 = i0; pPoseEntry->i1 = i1; pPoseEntry->s0 = s0; pPoseEntry->s1 = s1; pPoseEntry->animIndices[0] = -1; pPoseEntry->animIndices[1] = -1; pPoseEntry->animIndices[2] = -1; pPoseEntry->animIndices[3] = -1; pPoseEntry->anims[0].seqdesc_weight = 0.0f; pPoseEntry->anims[1].seqdesc_weight = 0.0f; pPoseEntry->anims[2].seqdesc_weight = 0.0f; pPoseEntry->anims[3].seqdesc_weight = 0.0f; pPoseEntry->anims[0].animstudiohdr_numbones = 0; pPoseEntry->anims[1].animstudiohdr_numbones = 0; pPoseEntry->anims[2].animstudiohdr_numbones = 0; pPoseEntry->anims[3].animstudiohdr_numbones = 0; if( s0 < 0.001f ) { if( s1 < 0.001f ) { if( PoseIsAllZeros_PS3( pStudioHdr, sequence, seqdesc, i0, i1 ) ) { pPoseEntry->cpsPath = CPSPATH_FALSE; bResult = false; } else { pPoseEntry->cpsPath = CPSPATH_CALCANIM1; pPoseEntry->animIndices[0] = 0; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1, 0, 1.0f ); } } else if( s1 > 0.999f ) { pPoseEntry->cpsPath = CPSPATH_CALCANIM1; pPoseEntry->animIndices[0] = 2; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1+1, 2, 1.0f ); } else { pPoseEntry->cpsPath = CPSPATH_CALCANIM2_S1; pPoseEntry->animIndices[0] = 0; pPoseEntry->animIndices[1] = 2; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1, 0, 1.0f - s1 ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1+1, 2, s1 ); } } else if( s0 > 0.999f ) { if( s1 < 0.001f ) { if( PoseIsAllZeros_PS3( pStudioHdr, sequence, seqdesc, i0+1, i1 ) ) { pPoseEntry->cpsPath = CPSPATH_FALSE; bResult = false; } else { pPoseEntry->cpsPath = CPSPATH_CALCANIM1; pPoseEntry->animIndices[0] = 1; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1, 1, 1.0f ); } } else if( s1 > 0.999f ) { pPoseEntry->cpsPath = CPSPATH_CALCANIM1; pPoseEntry->animIndices[0] = 3; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1+1, 3, 1.0f ); } else { pPoseEntry->cpsPath = CPSPATH_CALCANIM2_S1; pPoseEntry->animIndices[0] = 1; pPoseEntry->animIndices[1] = 3; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1, 1, 1.0f - s1 ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1+1, 3, s1 ); } } else { if( s1 < 0.001f ) { pPoseEntry->animIndices[0] = 0; pPoseEntry->animIndices[1] = 1; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1, 0, 1.0f - s0 ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1, 1, s0 ); if( PoseIsAllZeros_PS3( pStudioHdr, sequence, seqdesc, i0+1, i1 ) ) { pPoseEntry->cpsPath = CPSPATH_CALCANIM1_SCALE1MINUSS0; } else if( PoseIsAllZeros_PS3( pStudioHdr, sequence, seqdesc, i0, i1 ) ) { pPoseEntry->cpsPath = CPSPATH_CALCANIM1_SCALES0; } else { pPoseEntry->cpsPath = CPSPATH_CALCANIM2_S0; } } else if( s1 > 0.999f ) { pPoseEntry->cpsPath = CPSPATH_CALCANIM2_S0; pPoseEntry->animIndices[0] = 2; pPoseEntry->animIndices[1] = 3; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1+1, 2, 1.0f - s0 ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1+1, 3, s0 ); } else if( !anim_3wayblend.GetBool() ) { pPoseEntry->cpsPath = CPSPATH_CALCANIM4; pPoseEntry->animIndices[0] = 0; pPoseEntry->animIndices[1] = 1; pPoseEntry->animIndices[2] = 2; pPoseEntry->animIndices[3] = 3; SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1, 0, (1.0f - s0) * (1.0f - s1) ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1, 1, (s0) * (1.0f - s1) ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1+1, 2, (1.0f - s0) * (s1) ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1+1, 3, (s0) * (s1) ); } else { pPoseEntry->cpsPath = CPSPATH_CALCANIM3; pPoseEntry->animIndices[0] = 0; pPoseEntry->animIndices[1] = 1; pPoseEntry->animIndices[2] = 2; pPoseEntry->animIndices[3] = 3; // weights get fixed up later SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1, 0, 0.0f ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1, 1, 0.0f ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0, i1+1, 2, 0.0f ); SetAnimData( pPoseEntry, pStudioHdr, seqdesc, sequence, i0+1, i1+1, 3, 0.0f ); } } if( pPoseEntry->animIndices[0] == -1 ) { // ik may use the zeroth entry - why? virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); if( pVModel ) { int baseanimation = pStudioHdr->iRelativeAnim( sequence, seqdesc.anim( i0, i1 ) ); mstudioanimdesc_t &animdesc = ((CStudioHdr *)pStudioHdr)->pAnimdesc( baseanimation ); pPoseEntry->anims[ 0 ].pEA_animdesc = &animdesc; } else { mstudioanimdesc_t &animdesc = ((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( i0, i1 ) ); pPoseEntry->anims[ 0 ].pEA_animdesc = &animdesc; } } // Real IK weights // 0 : (1.0f - s0) * (1.0f - s1); // 1 : (s0) * (1.0f - s1); // 2 : (1.0f - s0) * (s1); // 3 : (s0) * (s1); return bResult; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void AddSequenceLocks_PS3( accumposeentry_SPU* pPoseEntry, const CStudioHdr* pStudioHdr, mstudioseqdesc_t &seqdesc ) { pPoseEntry->pEA_seqdesc_iklocks = NULL; if( pStudioHdr->numikchains() == 0) { return; } if( seqdesc.numiklocks == 0 ) { return; } pPoseEntry->pEA_seqdesc_iklocks = seqdesc.pIKLock( 0 ); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void AddAutoLocks_PS3( bonejob_SPU *pBonejob, const CStudioHdr* pStudioHdr ) { SNPROF_ANIM("AddAutoLocks_PS3"); if( pStudioHdr->GetNumIKAutoplayLocks() == 0 ) { return; } AssertFatal( pStudioHdr->GetNumIKAutoplayLocks() < MAX_IKLOCKS ); for( int i = 0; i < pStudioHdr->GetNumIKAutoplayLocks(); i++ ) { const mstudioiklock_t &lock = ((CStudioHdr *)pStudioHdr)->pIKAutoplayLock( i ); pBonejob->pEA_IKAutoplayLocks[ i ] = (void *)&lock; } } //----------------------------------------------------------------------------- // Add an AccumulatePose call to the bonejob //----------------------------------------------------------------------------- void CBoneSetup_PS3::AccumulatePose_AddToBoneJob( bonejob_SPU* pSPUJob, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel ) { SNPROF_ANIM( "CBoneSetup_PS3::AccumulatePose_AddToBoneJob" ); if( pSPUJob->numTotalPoses >= MAX_ACCUMPOSECALLS ) { m_errorFlags = BONEJOB_ERROR_EXCEEDEDMAXCALLS; return; } if( ( pqStackLevel+1 ) >= MAX_PQSTACKLEVEL ) { // pq stack depth exceeded, early out of job, run from scratch on PPU m_errorFlags = BONEJOB_ERROR_EXCEEDEDPQSTACK; return; } flWeight = clamp( flWeight, 0.0f, 1.0f ); if( sequence < 0 ) { // just skip, no error flags? return; } if( sequence >= m_pStudioHdr->GetNumSeq() ) { sequence = 0; } //------------------------------------------------------------------------------------- // get stack entry accumposeentry_SPU *pPoseEntry = &pSPUJob->accumPoseEntry[ pSPUJob->numTotalPoses++ ]; //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // copy call params and grab common data and ptrs for SPU path mstudioseqdesc_t &seqdesc = ((CStudioHdr *)m_pStudioHdr)->pSeqdesc( sequence ); pPoseEntry->pEA_seqdesc = &seqdesc; pPoseEntry->iSeq = sequence; pPoseEntry->cycle = cycle; pPoseEntry->weight = flWeight; pPoseEntry->pqStackLevel = pqStackLevel; pPoseEntry->seqdesc_numiklocks = seqdesc.numiklocks; pPoseEntry->seqdesc_numikrules = seqdesc.numikrules; pPoseEntry->seqdesc_flags = seqdesc.flags; virtualmodel_t *pVModel = m_pStudioHdr->GetVirtualModel(); const virtualgroup_t *pSeqGroup = NULL; pPoseEntry->pEA_seqgroup_boneMap = NULL; pPoseEntry->pEA_seqgroup_masterBone = NULL; if( pVModel ) { const studiohdr_t *pSeqStudioHdr; pSeqGroup = pVModel->pSeqGroup( sequence ); if( pSeqGroup ) { pPoseEntry->pEA_seqgroup_boneMap = (void *)pSeqGroup->boneMap.Base(); pPoseEntry->pEA_seqgroup_masterBone = (void *)pSeqGroup->masterBone.Base(); } pSeqStudioHdr = ((CStudioHdr *)m_pStudioHdr)->pSeqStudioHdr( sequence ); mstudiolinearbone_t *pLinearBones = pSeqStudioHdr->pLinearBones(); pPoseEntry->pEA_seq_linearBones = pLinearBones; if( pLinearBones ) { pPoseEntry->pEA_seq_linearbones_pos = (void *)(((byte *)pLinearBones) + pLinearBones->posindex); pPoseEntry->pEA_seq_linearbones_quat = (void *)(((byte *)pLinearBones) + pLinearBones->quatindex); } pPoseEntry->pEA_seq_bones_pos = &pSeqStudioHdr->pBone(0)->pos; } pPoseEntry->pEA_seqdesc_boneWeight = (void *)seqdesc.pBoneweight( 0 ); //------------------------------------------------------------------------------------- if( seqdesc.numiklocks ) { AddSequenceLocks_PS3( pPoseEntry, m_pStudioHdr, seqdesc ); } // potentially recurse if( CalcPoseSingle( pPoseEntry, m_pStudioHdr, seqdesc, sequence, cycle, m_flPoseParameter, pSPUJob->currentTime ) ) { pPoseEntry->numLocalLayers = AddLocalLayers( pSPUJob, seqdesc, sequence, cycle, 1.0f, pIKContext, pqStackLevel+1 ); } pPoseEntry->numSequenceLayers = AddSequenceLayers( pSPUJob, seqdesc, sequence, cycle, flWeight, pIKContext, pqStackLevel+1 ); // update bonejob max bones for( int lp = 0; lp < MAX_BLENDANIMS; lp++ ) { pSPUJob->maxBones = MAX( pPoseEntry->anims[lp].animstudiohdr_numbones, pSPUJob->maxBones ); } } //----------------------------------------------------------------------------- // Purpose: calculate a pose for a single sequence // adds autolayers, runs local ik rukes //----------------------------------------------------------------------------- int CBoneSetup_PS3::AddSequenceLayers( bonejob_SPU *pSPUJob, mstudioseqdesc_t &seqdesc, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel ) { SNPROF_ANIM("CBoneSetup_PS3::AddSequenceLayers"); int ret = 0; for (int i = 0; i < seqdesc.numautolayers; i++) { mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i ); if (pLayer->flags & STUDIO_AL_LOCAL) continue; float layerCycle = cycle; float layerWeight = flWeight; if (pLayer->start != pLayer->end) { float s = 1.0f; float index; if (!(pLayer->flags & STUDIO_AL_POSE)) { index = cycle; } else { int iSequence = m_pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); int iPose = m_pStudioHdr->GetSharedPoseParameter( iSequence, pLayer->iPose ); if (iPose != -1) { const mstudioposeparamdesc_t &Pose = ((CStudioHdr *)m_pStudioHdr)->pPoseParameter( iPose ); index = m_flPoseParameter[ iPose ] * (Pose.end - Pose.start) + Pose.start; } else { index = 0; } } if (index < pLayer->start) continue; if (index >= pLayer->end) continue; if (index < pLayer->peak && pLayer->start != pLayer->peak) { s = (index - pLayer->start) / (pLayer->peak - pLayer->start); } else if (index > pLayer->tail && pLayer->end != pLayer->tail) { s = (pLayer->end - index) / (pLayer->end - pLayer->tail); } if (pLayer->flags & STUDIO_AL_SPLINE) { s = SimpleSpline( s ); } if ((pLayer->flags & STUDIO_AL_XFADE) && (index > pLayer->tail)) { layerWeight = ( s * flWeight ) / ( 1.0f - flWeight + s * flWeight ); } else if (pLayer->flags & STUDIO_AL_NOBLEND) { layerWeight = s; } else { layerWeight = flWeight * s; } if (!(pLayer->flags & STUDIO_AL_POSE)) { layerCycle = (cycle - pLayer->start) / (pLayer->end - pLayer->start); } } ret++; int iSequence = m_pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); AccumulatePose_AddToBoneJob( pSPUJob, iSequence, layerCycle, layerWeight, pIKContext, pqStackLevel ); } return ret; } //----------------------------------------------------------------------------- // Purpose: calculate a pose for a single sequence // adds autolayers, runs local ik rukes //----------------------------------------------------------------------------- int CBoneSetup_PS3::AddLocalLayers( bonejob_SPU *pSPUJob, mstudioseqdesc_t &seqdesc, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel ) { SNPROF_ANIM("CBoneSetup_PS3::AddLocalLayers"); int ret = 0; if (!(seqdesc.flags & STUDIO_LOCAL)) { return ret; } for (int i = 0; i < seqdesc.numautolayers; i++) { mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i ); if (!(pLayer->flags & STUDIO_AL_LOCAL)) continue; float layerCycle = cycle; float layerWeight = flWeight; if (pLayer->start != pLayer->end) { float s = 1.0f; if (cycle < pLayer->start) continue; if (cycle >= pLayer->end) continue; if (cycle < pLayer->peak && pLayer->start != pLayer->peak) { s = (cycle - pLayer->start) / (pLayer->peak - pLayer->start); } else if (cycle > pLayer->tail && pLayer->end != pLayer->tail) { s = (pLayer->end - cycle) / (pLayer->end - pLayer->tail); } if (pLayer->flags & STUDIO_AL_SPLINE) { s = SimpleSpline( s ); } if ((pLayer->flags & STUDIO_AL_XFADE) && (cycle > pLayer->tail)) { layerWeight = ( s * flWeight ) / ( 1.0f - flWeight + s * flWeight ); } else if (pLayer->flags & STUDIO_AL_NOBLEND) { layerWeight = s; } else { layerWeight = flWeight * s; } layerCycle = (cycle - pLayer->start) / (pLayer->end - pLayer->start); } ret++; int iSequence = m_pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); AccumulatePose_AddToBoneJob( pSPUJob, iSequence, layerCycle, layerWeight, pIKContext, pqStackLevel ); } return ret; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- IBoneSetup_PS3::IBoneSetup_PS3( const CStudioHdr *pStudioHdr, int boneMask, const float poseParameter[], bonejob_SPU *pBoneJobSPU ) { m_pBoneSetup = new CBoneSetup_PS3( pStudioHdr, boneMask, poseParameter, pBoneJobSPU ); } IBoneSetup_PS3::~IBoneSetup_PS3( void ) { if ( m_pBoneSetup ) { delete m_pBoneSetup; } } void IBoneSetup_PS3::InitPose_PS3( BoneVector pos[], BoneQuaternion q[] ) { ::InitPose_PS3( m_pBoneSetup->m_pStudioHdr, pos, q, m_pBoneSetup->m_boneMask ); } void IBoneSetup_PS3::CalcAutoplaySequences_AddPoseCalls( float flRealTime ) { m_pBoneSetup->CalcAutoplaySequences_AddPoseCalls( flRealTime ); } void IBoneSetup_PS3::AccumulatePose_AddToBoneJob( bonejob_SPU* pBonejobSPU, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel ) { m_pBoneSetup->AccumulatePose_AddToBoneJob( pBonejobSPU, sequence, cycle, flWeight, pIKContext, pqStackLevel ); } CStudioHdr *IBoneSetup_PS3::GetStudioHdr() { return (CStudioHdr *)m_pBoneSetup->m_pStudioHdr; } void IBoneSetup_PS3::ResetErrorFlags() { m_pBoneSetup->m_errorFlags = 0; } int IBoneSetup_PS3::ErrorFlags() { return m_pBoneSetup->m_errorFlags; } bonejob_SPU *IBoneSetup_PS3::GetBoneJobSPU() { return m_pBoneSetup->m_pBoneJobSPU; } int IBoneSetup_PS3::RunAccumulatePoseJobs_PPU( bonejob_SPU *pBonejobSPU ) { return m_pBoneSetup->RunAccumulatePoseJobs_PPU( pBonejobSPU ); } int IBoneSetup_PS3::RunAccumulatePoseJobs_SPU( bonejob_SPU *pBoneJobSPU, job_accumpose::JobDescriptor_t *pJobDescriptor ) { return m_pBoneSetup->RunAccumulatePoseJobs_SPU( pBoneJobSPU, pJobDescriptor ); } CBoneSetup_PS3::CBoneSetup_PS3( const CStudioHdr *pStudioHdr, int boneMask, const float poseParameter[], bonejob_SPU *pBoneJobSPU ) { m_pStudioHdr = pStudioHdr; m_boneMask = boneMask; m_flPoseParameter = poseParameter; m_pBoneJobSPU = pBoneJobSPU; m_errorFlags = 0; } //----------------------------------------------------------------------------- // Purpose: run all animations that automatically play and are driven off of poseParameters // This version adds AccumulatePose calls to the current bonejob //----------------------------------------------------------------------------- void CBoneSetup_PS3::CalcAutoplaySequences_AddPoseCalls( float flRealTime ) { SNPROF_ANIM( "CBoneSetup_PS3::CalcAutoplaySequences_AddPoseCalls" ); int i; AddAutoLocks_PS3( m_pBoneJobSPU, m_pStudioHdr ); unsigned short *pList = NULL; int count = m_pStudioHdr->GetAutoplayList( &pList ); for( i = 0; i < count; i++ ) { int sequenceIndex = pList[i]; mstudioseqdesc_t &seqdesc = ((CStudioHdr *)m_pStudioHdr)->pSeqdesc( sequenceIndex ); if( seqdesc.flags & STUDIO_AUTOPLAY ) { float cycle = 0.0f; float cps = Studio_CPS( m_pStudioHdr, seqdesc, sequenceIndex, m_flPoseParameter ); cycle = flRealTime * cps; cycle = cycle - (int)cycle; // AccumulatePose( pos, q, sequenceIndex, cycle, 1.0, flRealTime, pIKContext ); // null ik context since dependencies are added but not used // still need autoplaylocks added/solved AccumulatePose_AddToBoneJob( m_pBoneJobSPU, sequenceIndex, cycle, 1.0f, NULL, 0 ); } } } void MsgJobData( bonejob_SPU *pBoneJob ) { int lp; Msg("**********************************************************\n"); Msg(" BoneJob Data Start\n"); Msg("**********************************************************\n"); Msg("numikAutoplayLocks: %d\n", pBoneJob->numikAutoplayLocks); for(lp = 0; lp< pBoneJob->numikAutoplayLocks; lp++) { Msg("pEA_IKAutoplayLocks[%d] = 0x%x\n", lp, (unsigned int)pBoneJob->pEA_IKAutoplayLocks[lp]); } Msg("numTotalPoses: %d\n", pBoneJob->numTotalPoses ); for(lp = 0; lp< pBoneJob->numTotalPoses; lp++) { accumposeentry_SPU *pPoseEntry = &pBoneJob->accumPoseEntry[lp]; Msg("PoseEntry[%d]\n",lp); Msg("--------------\n"); Msg("pEA_seqdesc = 0x%x\n", (unsigned int)pPoseEntry->pEA_seqdesc); Msg("pEA_seqgroup_boneMap = 0x%x\n", (unsigned int)pPoseEntry->pEA_seqgroup_boneMap); Msg("pEA_seqgroup_masterBone = 0x%x\n", (unsigned int)pPoseEntry->pEA_seqgroup_masterBone); Msg("pEA_seqdesc_boneWeight = 0x%x\n", (unsigned int)pPoseEntry->pEA_seqdesc_boneWeight); Msg("pEA_seqdesc_iklocks = 0x%x\n", (unsigned int)pPoseEntry->pEA_seqdesc_iklocks); Msg("pEA_seq_linearBones = 0x%x\n", (unsigned int)pPoseEntry->pEA_seq_linearBones); Msg("pEA_seq_linearbones_pos = 0x%x\n", (unsigned int)pPoseEntry->pEA_seq_linearbones_pos); Msg("pEA_seq_linearbones_quat = 0x%x\n", (unsigned int)pPoseEntry->pEA_seq_linearbones_quat); Msg("pEA_seq_bones_pos = 0x%x\n", (unsigned int)pPoseEntry->pEA_seq_bones_pos); switch( pPoseEntry->cpsPath ) { case CPSPATH_FALSE: break; case CPSPATH_CALCANIM1: case CPSPATH_CALCANIM1_SCALE1MINUSS0: case CPSPATH_CALCANIM1_SCALES0: break; case CPSPATH_CALCANIM2_S0: case CPSPATH_CALCANIM2_S1: break; case CPSPATH_CALCANIM3: case CPSPATH_CALCANIM4: break; default: break; } } Msg("**********************************************************\n"); Msg(" BoneJob Data End\n"); Msg("**********************************************************\n"); } //-------------------------------------------------------------------------------------------------------------------- // Entry point for running bone jobs, spu jobs pushed from here // //-------------------------------------------------------------------------------------------------------------------- int CBoneSetup_PS3::RunAccumulatePoseJobs_SPU( bonejob_SPU *pBoneJob, job_accumpose::JobDescriptor_t *pJobDescriptor ) { SNPROF_ANIM("CBoneSetup_PS3::RunAccumulatePoseJobs_SPU"); ConVarRef cl_PS3_SPU_bones_debug( "cl_PS3_SPU_bones_debug" ); ConVarRef cl_PS3_SPU_bones_minbonecount( "cl_PS3_SPU_bones_minbonecount" ); int numBones = pBoneJob->numBones; // don't push jobs with less than minbonecount bones to SPU, just perform the job inline on PPU, need to find a sweet spot for minbonecount if( numBones < cl_PS3_SPU_bones_minbonecount.GetInt() ) { return RunAccumulatePoseJobs_PPU( pBoneJob ); } BoneVector *pos = (BoneVector *)pBoneJob->pEA_pos; BoneQuaternion *q = (BoneQuaternion *)pBoneJob->pEA_q; if( cl_PS3_SPU_bones_debug.GetInt() == 2 ) { memcpy( g_posInit, pos, sizeof(BoneVector) * numBones ); memcpy( g_qInit, q, sizeof(BoneQuaternion) * numBones ); } // initialise header pJobDescriptor->header = g_AccumPoseJobDescriptor.header; pJobDescriptor->header.useInOutBuffer = 1; pJobDescriptor->header.sizeStack = (16*1024)/8; pJobDescriptor->header.sizeInOrInOut = 0; pJobDescriptor->header.sizeDmaList = 0; // add dma's AddInputDma( pJobDescriptor, sizeof(bonejob_SPU), pBoneJob ); AddInputDma( pJobDescriptor, sizeof(BoneVector) * pBoneJob->numBones, pBoneJob->pEA_pos ); AddInputDma( pJobDescriptor, sizeof(BoneQuaternion) * pBoneJob->numBones, pBoneJob->pEA_q ); pJobDescriptor->header.sizeInOrInOut += (sizeof(ikcontextikrule_t) * MAX_IKRULES) + (sizeof(int) * 4); int jobPort = g_pBoneJobs->GetNextFreeSPURSPort(); // kick off job //Msg("push job port %d, numbones %d\n", jobPort, pBoneJob->numBones ); CELL_VERIFY( g_pBoneJobs->m_pRoot->m_queuePortAnim[ jobPort ].pushJob( &pJobDescriptor->header, sizeof(*pJobDescriptor), 0, CELL_SPURS_JOBQUEUE_FLAG_SYNC_JOB ) ); if( cl_PS3_SPU_bones_debug.GetInt() ) { //Msg("_DEBUG_ACCUMPOSE sync job %d\n", jobPort); CELL_VERIFY( g_pBoneJobs->m_pRoot->m_queuePortAnim[ jobPort ].sync( 0 ) ); if( cl_PS3_SPU_bones_debug.GetInt() > 1 ) { // reset pos, q memcpy( pos, g_posInit, sizeof(BoneVector) * numBones ); memcpy( q, g_qInit, sizeof(BoneQuaternion) * numBones ); RunAccumulatePoseJobs_PPU( pBoneJob ); } } return jobPort; } //-------------------------------------------------------------------------------------------------------------------- // Entry point for running bone jobs // This is the PPU version // //-------------------------------------------------------------------------------------------------------------------- int CBoneSetup_PS3::RunAccumulatePoseJobs_PPU( bonejob_SPU *pBoneJob ) { SNPROF_ANIM("CBoneSetup_PS3::RunAccumulatePoseJobs_PPU"); InitLSStack(); BoneVector *pos = (BoneVector *)pBoneJob->pEA_pos; BoneQuaternion *q = (BoneQuaternion *)pBoneJob->pEA_q; g_addDep_numIKRules = 0; // pos and q already contain result of initial ::init, which all subsequent calls of ::init take a copy of // don't write over until the end when it should contain the final pos, q data C_AccumulatePose_SPU cAccumPose( pBoneJob ); int numBones = pBoneJob->numBones; // backup memcpy( g_posInit, pos, sizeof(BoneVector) * numBones ); memcpy( g_qInit, q, sizeof(BoneQuaternion) * numBones ); // into pq stack which we work on BoneVector *pos0 = (BoneVector *)PushLSStack( sizeof(BoneVector) * numBones ); BoneQuaternion *q0 = (BoneQuaternion *)PushLSStack( sizeof(BoneQuaternion) * numBones ); memcpy( pos0, pos, sizeof(BoneVector) * numBones ); memcpy( q0, q, sizeof(BoneQuaternion) * numBones ); cAccumPose.ResetCount(); cAccumPose.SetIKContext( pBoneJob->pEA_IKContext ); while( cAccumPose.GetCount() < pBoneJob->numPoses_PreAutoSeq ) { cAccumPose.AccumulatePose( pos0, q0 ); } CIKContext_PS3 auto_ik; auto_ik.Init( pBoneJob, pBoneJob->autoikAngles, pBoneJob->autoikOrigin, pBoneJob->currentTime, pBoneJob->autoikFramecount, pBoneJob->boneMask ); auto_ik.AddAutoplayLocks( pBoneJob, pos0, q0 ); while( cAccumPose.GetCount() < pBoneJob->numTotalPoses ) { cAccumPose.AccumulatePose( pos0, q0 ); } auto_ik.SolveAutoplayLocks( pBoneJob, pos0, q0 ); // write results to pos, q memcpy( pos, pos0, sizeof( BoneVector ) * numBones ); memcpy( q, q0, sizeof( BoneQuaternion ) * numBones ); // write AddDependencies() potential IK rules if( g_addDep_numIKRules ) { memcpy( pBoneJob->pEA_addDep_numIKRules, &g_addDep_numIKRules, sizeof(int) ); memcpy( pBoneJob->pEA_addDep_IKRules, g_addDep_IKRules, g_addDep_numIKRules * sizeof(ikcontextikrule_t_PS3) ); } PopLSStack( sizeof(BoneQuaternion) * numBones ); PopLSStack( sizeof(BoneVector) * numBones ); TermLSStack(); return VJobsRoot::MAXPORTS_ANIM; // => can only be a ppu job } #endif // #if !defined(__SPU__) //-------------------------------------------------------------------------------------------------------------------- // // Below this point needs to go to SPU, above this point PPU only (prep pass for SPU) // //-------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Purpose: turn a 2x2 blend into a 3 way triangle blend // Returns: returns the animation indices and barycentric coordinates of a triangle // the triangle is a right triangle, and the diagonal is between elements [0] and [2] //----------------------------------------------------------------------------- void Calc3WayBlendIndices_SPU( accumposeentry_SPU *pPoseEntry, int i0, int i1, float s0, float s1, int *pAnimIndices, float *pWeight ) { // Figure out which bi-section direction we are using to make triangles. bool bEven = ( ( ( i0 + i1 ) & 0x1 ) == 0 ); // diagonal is between elements 1 & 3 // TL to BR if ( bEven ) { if ( s0 > s1 ) { // B pAnimIndices[0] = pPoseEntry->animIndices[0];//0; pAnimIndices[1] = pPoseEntry->animIndices[1];//1; pAnimIndices[2] = pPoseEntry->animIndices[3];//3; pWeight[0] = (1.0f - s0); pWeight[1] = s0 - s1; } else { // C pAnimIndices[0] = pPoseEntry->animIndices[3];//3; pAnimIndices[1] = pPoseEntry->animIndices[2];//2; pAnimIndices[2] = pPoseEntry->animIndices[0];//0; pWeight[0] = s0; pWeight[1] = s1 - s0; } } // BL to TR else { float flTotal = s0 + s1; if( flTotal > 1.0f ) { // D pAnimIndices[0] = pPoseEntry->animIndices[1];//1; pAnimIndices[1] = pPoseEntry->animIndices[3];//3; pAnimIndices[2] = pPoseEntry->animIndices[2];//2; pWeight[0] = (1.0f - s1); pWeight[1] = s0 - 1.0f + s1; } else { // A pAnimIndices[0] = pPoseEntry->animIndices[2];//2; pAnimIndices[1] = pPoseEntry->animIndices[0];//0; pAnimIndices[2] = pPoseEntry->animIndices[1];//1; pWeight[0] = s1; pWeight[1] = 1.0f - s0 - s1; } } // clamp the diagonal if( pWeight[1] < 0.001f ) pWeight[1] = 0.0f; pWeight[2] = 1.0f - pWeight[0] - pWeight[1]; Assert( pWeight[0] >= 0.0f && pWeight[0] <= 1.0f ); Assert( pWeight[1] >= 0.0f && pWeight[1] <= 1.0f ); Assert( pWeight[2] >= 0.0f && pWeight[2] <= 1.0f ); // fix up pose entry weights so that IK rules are blended correctly (assume they all come in as zero for the 3 way blend case) pPoseEntry->anims[ pAnimIndices[0] ].seqdesc_weight = pWeight[0]; pPoseEntry->anims[ pAnimIndices[1] ].seqdesc_weight = pWeight[1]; pPoseEntry->anims[ pAnimIndices[2] ].seqdesc_weight = pWeight[2]; } void C_AccumulatePose_SPU::AddLocalLayers( accumposeentry_SPU *pAccPoseEntry, BoneVector pos[], BoneQuaternion q[] ) { for( int lp = 0; lp < pAccPoseEntry->numLocalLayers; lp++ ) { AccumulatePose( pos, q ); } } void C_AccumulatePose_SPU::AddSequenceLayers( accumposeentry_SPU *pAccPoseEntry, BoneVector pos[], BoneQuaternion q[] ) { for( int lp = 0; lp < pAccPoseEntry->numSequenceLayers; lp++ ) { AccumulatePose( pos, q ); } } //----------------------------------------------------------------------------- // Purpose: calculate a pose for a single sequence // SPU pass //----------------------------------------------------------------------------- bool C_AccumulatePose_SPU::CalcPoseSingle( const bonejob_SPU *pBonejob, accumposeentry_SPU *pPoseEntry, BoneVector *pos, BoneQuaternion *q, int *boneMap, float *boneWeight, int sequence, const float poseParameter[], int boneMask, float flTime ) { SNPROF_ANIM("C_AccumulatePose_SPU::CalcPoseSingle"); bool bResult = true; BoneVector *pos2 = (BoneVector *)PushLSStack( sizeof(BoneVector) * pBonejob->numBones ); BoneQuaternion *q2 = (BoneQuaternion *)PushLSStack( sizeof(BoneQuaternion) * pBonejob->numBones ); BoneVector *pos3 = (BoneVector *)PushLSStack( sizeof(BoneVector) * pBonejob->numBones ); BoneQuaternion *q3 = (BoneQuaternion *)PushLSStack( sizeof(BoneQuaternion) * pBonejob->numBones ); float s0 = pPoseEntry->s0; float s1 = pPoseEntry->s1; int i0 = pPoseEntry->i0; int i1 = pPoseEntry->i1; float cycle = pPoseEntry->cyclePoseSingle; switch( pPoseEntry->cpsPath ) { case CPSPATH_FALSE: bResult = false; break; case CPSPATH_CALCANIM1: CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, pPoseEntry->animIndices[0], cycle, boneMask ); break; case CPSPATH_CALCANIM1_SCALE1MINUSS0: CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, pPoseEntry->animIndices[0], cycle, boneMask ); ScaleBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, 1.0f - s0, boneMask ); break; case CPSPATH_CALCANIM1_SCALES0: CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, pPoseEntry->animIndices[0], cycle, boneMask ); ScaleBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, s0, boneMask ); break; case CPSPATH_CALCANIM2_S0: CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, pPoseEntry->animIndices[0], cycle, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos2, q2, boneMap, boneWeight, pPoseEntry->animIndices[1], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q2, pos2, s0, boneMask ); break; case CPSPATH_CALCANIM2_S1: CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, pPoseEntry->animIndices[0], cycle, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos2, q2, boneMap, boneWeight, pPoseEntry->animIndices[1], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q2, pos2, s1, boneMask ); break; case CPSPATH_CALCANIM3: int iAnimIndices[3]; float weight[3]; Calc3WayBlendIndices_SPU( pPoseEntry, i0, i1, s0, s1, iAnimIndices, weight ); if( weight[1] < 0.001f ) { // on diagonal CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, iAnimIndices[0], cycle, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos2, q2, boneMap, boneWeight, iAnimIndices[2], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q2, pos2, weight[2] / (weight[0] + weight[2]), boneMask ); } else { CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, iAnimIndices[0], cycle, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos2, q2, boneMap, boneWeight, iAnimIndices[1], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q2, pos2, weight[1] / (weight[0] + weight[1]), boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos3, q3, boneMap, boneWeight, iAnimIndices[2], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q3, pos3, weight[2], boneMask ); } break; case CPSPATH_CALCANIM4: CalcAnimation_PS3( pBonejob, pPoseEntry, pos, q, boneMap, boneWeight, pPoseEntry->animIndices[0], cycle, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos2, q2, boneMap, boneWeight, pPoseEntry->animIndices[1], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q2, pos2, s0, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos2, q2, boneMap, boneWeight, pPoseEntry->animIndices[2], cycle, boneMask ); CalcAnimation_PS3( pBonejob, pPoseEntry, pos3, q3, boneMap, boneWeight, pPoseEntry->animIndices[3], cycle, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q2, pos2, boneMap, boneWeight, q3, pos3, s0, boneMask ); BlendBones_PS3( pBonejob, pPoseEntry, q, pos, boneMap, boneWeight, q2, pos2, s1, boneMask ); break; default: bResult = false; break; } PopLSStack( sizeof(BoneQuaternion) * pBonejob->numBones ); PopLSStack( sizeof(BoneVector) * pBonejob->numBones ); PopLSStack( sizeof(BoneQuaternion) * pBonejob->numBones ); PopLSStack( sizeof(BoneVector) * pBonejob->numBones ); return bResult; } //----------------------------------------------------------------------------- // SPU/bonejob version of AccumulatePose // Assumes bonejob calls to this happen in the correct order (recursion has happened during the 1st/prep pass on PPU) // individual AccumulatePose call data is stored in bonejob->accumPoseEntry //----------------------------------------------------------------------------- void C_AccumulatePose_SPU::AccumulatePose( BoneVector pos[], BoneQuaternion q[] ) { SNPROF_ANIM("C_AccumulatePose_SPU::AccumulatePose"); accumposeentry_SPU *pPoseEntry = &m_pBoneJob->accumPoseEntry[ m_iCount++ ]; int boneMask = m_pBoneJob->boneMask; int numBones = m_pBoneJob->numBones; float time = m_pBoneJob->currentTime; float *pPoseParam = m_pBoneJob->poseparam; float weight = pPoseEntry->weight; int sequence = pPoseEntry->iSeq; void *pIKContext = GetIKContext(); CIKContext_PS3 seq_ik; if( pPoseEntry->seqdesc_numiklocks ) { //IKOFF seq_ik.Init( m_pBoneJob, vec3_angle, vec3_origin, 0.0f, 0, boneMask ); //IKOFF seq_ik.AddSequenceLocks( m_pBoneJob, pPoseEntry, pos, q ); } { BoneVector *pos2 = (BoneVector *)PushLSStack( sizeof(BoneVector) * m_pBoneJob->maxBones ); BoneQuaternion *q2 = (BoneQuaternion *)PushLSStack( sizeof(BoneQuaternion) * m_pBoneJob->maxBones ); int *pBoneMap = (int *)PushLSStack( sizeof(int) * m_pBoneJob->maxBones ); float *pBoneWeight = (float *)PushLSStack( sizeof(float) * m_pBoneJob->maxBones ); // used in calcposesingle (calcanim/calcvirtualanim) & SlerpBones // needs to be stack based, as AddLocalLayers may recurse AccumulatePose... GetBoneMapBoneWeight_SPU( m_pBoneJob, pPoseEntry, pBoneMap, pBoneWeight ); if( pPoseEntry->seqdesc_flags & STUDIO_LOCAL ) { // ::init memcpy( pos2, g_posInit, sizeof( BoneVector ) * numBones ); memcpy( q2, g_qInit, sizeof( BoneQuaternion ) * numBones ); } if( CalcPoseSingle( m_pBoneJob, pPoseEntry, pos2, q2, pBoneMap, pBoneWeight, sequence, pPoseParam, boneMask, time ) ) { AddLocalLayers( pPoseEntry, pos2, q2 ); SlerpBones_SPU( m_pBoneJob, pPoseEntry, q, pos, q2, pos2, pBoneMap, pBoneWeight, weight, boneMask ); } PopLSStack( sizeof(float) * m_pBoneJob->maxBones ); PopLSStack( sizeof(int) * m_pBoneJob->maxBones ); PopLSStack( sizeof(BoneQuaternion) * m_pBoneJob->maxBones ); PopLSStack( sizeof(BoneVector) * m_pBoneJob->maxBones ); } if( pIKContext ) { //IKOFF AddDependencies_SPU( m_pBoneJob, pPoseEntry, weight ); } AddSequenceLayers( pPoseEntry, pos, q ); if( pPoseEntry->seqdesc_numiklocks ) { //IKOFF seq_ik.SolveSequenceLocks( m_pBoneJob, pPoseEntry, pos, q ); } }