MF_API MFMatrix *MFAnimation_CalculateMatrices(MFAnimation *pAnimation, MFMatrix *pLocalToWorld) { MFAnimationBone *pAnims = pAnimation->pTemplate->pBones; MFModelBone *pBones = pAnimation->pBones; MFMatrix *pMats = pAnimation->pMatrices; float t = pAnimation->blendLayer.frameTime; MFDebug_Assert(t >= pAnimation->pTemplate->startTime && t <= pAnimation->pTemplate->endTime, "Frame time outside animation range..."); // find the frame number for each bone for(uint32 a=0; a<pAnimation->numBones; a++) { int map = pAnimation->pBoneMap[a]; if(map != -1) { float *pTimes = pAnims[map].pTime; int lastFrames = pAnims[map].numFrames-1; if(t == pTimes[lastFrames]) { pAnimation->blendLayer.pCurFrames[a].tweenStart = lastFrames; pAnimation->blendLayer.pCurFrames[a].tweenEnd = lastFrames; pAnimation->blendLayer.pCurFrames[a].tween = 0; } else { // TODO: change this to a binary search... for(int b=0; b<lastFrames; b++) { float t1 = pTimes[b]; float t2 = pTimes[b+1]; if(t >= pTimes[b] && t < pTimes[b+1]) { pAnimation->blendLayer.pCurFrames[a].tweenStart = b; pAnimation->blendLayer.pCurFrames[a].tweenEnd = b+1; pAnimation->blendLayer.pCurFrames[a].tween = (t-t1) / (t2-t1); break; } } } } } // calculate the matrix for each bone for(uint32 a=0; a<pAnimation->numBones; a++) { int map = pAnimation->pBoneMap[a]; if(map != -1) { MFMatrix &m1 = pAnims[map].pFrames[pAnimation->blendLayer.pCurFrames[a].tweenStart].key; MFMatrix &m2 = pAnims[map].pFrames[pAnimation->blendLayer.pCurFrames[a].tweenEnd].key; gWorkingMats[a].Tween(m1, m2, pAnimation->blendLayer.pCurFrames[a].tween); } else { gWorkingMats[a] = pBones[a].boneMatrix; } } // build the animation matrix for each bone... // TODO: this could be much faster for(uint32 a=0; a<pAnimation->numBones; a++) { MFMatrix boneMat = MFMatrix::identity; int b = (int)a; do { boneMat.Multiply(gWorkingMats[b]); b = pBones[b].parent; } while(b != -1); // pMats[a].Multiply(boneMat, pBones[a].invWorldMatrix); pMats[a].Multiply(pBones[a].invWorldMatrix, boneMat); if(pLocalToWorld) pMats[a].Multiply(*pLocalToWorld); } return pAnimation->pMatrices; }
const char *ParseFrame(const char *pText, const MFMatrix &mat, int parentID) { char frameName[64]; const char *pName = GetNextToken(pText, &pText, frameName); MFMatrix worldMatrix = mat; F3DBone *pBone = NULL; if(!MFString_CaseCmpN(pName, "bn_", 3) || !MFString_CaseCmpN(pName, "z_", 2)) { int boneID = pModel->GetSkeletonChunk()->bones.size(); pBone = &pModel->GetSkeletonChunk()->bones[boneID]; F3DBone *pParent = parentID == -1 ? NULL : &pModel->GetSkeletonChunk()->bones[parentID]; parentID = boneID; MFString_Copy(pBone->name, pName); MFString_Copy(pBone->parentName, pParent ? pParent->name : ""); pBone->worldMatrix = mat; } if(MFString_Compare(pName, "{")) SkipToken(pText, "{"); const char *pTok = GetNextToken(pText, &pText); while(MFString_Compare(pTok, "}")) { if(!MFString_Compare(pTok, "Frame")) { pText = ParseFrame(pText, worldMatrix, parentID); } else if(!MFString_Compare(pTok, "FrameTransformMatrix")) { SkipToken(pText, "{"); MFMatrix localMatrix; GetFloatArray(pText, (float*)&localMatrix, 16, &pText); worldMatrix.Multiply(localMatrix, worldMatrix); if(pBone) { pBone->boneMatrix = localMatrix; pBone->worldMatrix = worldMatrix; } SkipToken(pText, ";"); SkipToken(pText, "}"); } else if(!MFString_Compare(pTok, "Mesh")) { gMeshChunks.push(XMeshChunk::Create(worldMatrix, pText, pName)); SkipSection(pText); } else { MFDebug_Warn(4, MFStr("Unexpected token '%s'\n", pTok)); SkipSection(pText); } pTok = GetNextToken(pText, &pText); } return pText; }