সমস্যাটি ছিল এক সংখ্যাগত স্থায়িত্ব। 2 মাস ধরে এটিতে প্রায় 30 ঘন্টা কাজ হয়েছে, কেবল এটি শুরুর জন্যই আমি প্রথম থেকেই এটি করছি। আমি যখন ঘূর্ণন ম্যাট্রিকগুলি পুনঃনির্ধারণ কোডে প্লাগ করার আগে অর্থো-সাধারণীকরণ করি, তখন গুণমান উত্সের সহজ সমাধান * বিপরীত (লক্ষ্য) পুরোপুরি কার্যকর হয়েছিল। অবশ্যই, এর চেয়ে আরও অনেক বেশি প্রচলন রয়েছে (বিশেষত, কঙ্কালের বিভিন্ন আকার, যেমন কাঁধের প্রস্থ ইত্যাদি বিবেচনা করে)। যদি কেউ কৌতূহলী হয় তবে আমি সহজ, সরল পদ্ধতির জন্য কোডটি ব্যবহার করছি:
public static SkeletalAnimation retarget(SkeletalAnimation animation, Skeleton target, string boneMapFilePath)
{
if(animation == null) throw new ArgumentNullException("animation");
if(target == null) throw new ArgumentNullException("target");
Skeleton source = animation.skeleton;
if(source == target) return animation;
int nSourceBones = source.count;
int nTargetBones = target.count;
int nFrames = animation.nFrames;
AnimationData[] sourceData = animation.data;
Matrix[] sourceTransforms = new Matrix[nSourceBones];
Matrix[] targetTransforms = new Matrix[nTargetBones];
AnimationData[] temp = new AnimationData[nSourceBones];
AnimationData[] targetData = new AnimationData[nTargetBones * nFrames];
// Get a map where map[iTargetBone] = iSourceBone or -1 if no such bone
int[] map = parseBoneMap(source, target, boneMapFilePath);
for(int iFrame = 0; iFrame < nFrames; iFrame++)
{
int sourceBase = iFrame * nSourceBones;
int targetBase = iFrame * nTargetBones;
// Copy the root translation and rotation directly over
AnimationData rootData = targetData[targetBase] = sourceData[sourceBase];
// Get the source pose for this frame
Array.Copy(sourceData, sourceBase, temp, 0, nSourceBones);
source.getAbsoluteTransforms(temp, sourceTransforms);
// Rotate target bones to face that direction
Matrix m;
AnimationData.toMatrix(ref rootData, out m);
Matrix.Multiply(ref m, ref target.relatives[0], out targetTransforms[0]);
for(int iTargetBone = 1; iTargetBone < nTargetBones; iTargetBone++)
{
int targetIndex = targetBase + iTargetBone;
int iTargetParent = target.hierarchy[iTargetBone];
int iSourceBone = map[iTargetBone];
if(iSourceBone <= 0)
{
targetData[targetIndex].rotation = Quaternion.Identity;
Matrix.Multiply(ref target.relatives[iTargetBone], ref targetTransforms[iTargetParent], out targetTransforms[iTargetBone]);
}
else
{
Matrix currentTransform, inverseCurrent, sourceTransform, final, m2;
Quaternion rot;
// Get the "current" transformation (transform that would be applied if rot is Quaternion.Identity)
Matrix.Multiply(ref target.relatives[iTargetBone], ref targetTransforms[iTargetParent], out currentTransform);
Math2.orthoNormalize(ref currentTransform);
Matrix.Invert(ref currentTransform, out inverseCurrent);
Math2.orthoNormalize(ref inverseCurrent);
// Get the final rotation
Math2.orthoNormalize(ref sourceTransforms[iSourceBone], out sourceTransform);
Matrix.Multiply(ref sourceTransform, ref inverseCurrent, out final);
Math2.orthoNormalize(ref final);
Quaternion.RotationMatrix(ref final, out rot);
// Calculate this bone's absolute position to use as next bone's parent
targetData[targetIndex].rotation = rot;
Matrix.RotationQuaternion(ref rot, out m);
Matrix.Multiply(ref m, ref target.relatives[iTargetBone], out m2);
Matrix.Multiply(ref m2, ref targetTransforms[iTargetParent], out targetTransforms[iTargetBone]);
}
}
}
return new SkeletalAnimation(target, targetData, animation.fps, nFrames);
}