アニメーション コードで作業する際に、ボーン インデックスを目にすることが多くなります。数種類のボーン インデックスがあるため、初めてアニメーション コードを見ると混乱するかもしれません。そこで、ボーン インデックスが複数存在する理由と、それぞれの違いについて説明します。
アニメーション コードを見る際に重要となる 3 つの主要なボーン インデックスがあります。以下の 3 つです。
- Mesh Bone Index
- Skeleton Bone Index
- FCompactPoseBoneIndex
Mesh Bone Index は、スケルタルメッシュの一部です。これには全てのメッシュのジョイント、階層の順序が含まれ、スケルタルメッシュをレンダリングする際に使用されます。Mesh Bone Index を見るには、スケルタルメッシュをインポートし、それをペルソナで開きます。そうすると、含まれるすべてのジョイントを見ることができます。
Mesh Bone Index を使って SkeletalMesh コンポーネントの SpaceBases または LocalAtoms にアクセスします。
Mesh Bone Index がスケルタルメッシュの一部ならば、Skeleton Bone Index は何でしょうか? ペルソナを使ったことがあれば、USkeleton アセットのことはご存じですね。これは、スケルタルメッシュのインポート時、または既存のスケルタルメッシュを使って割り当てたときに作成されます。USkeleton アセットは USkeletalMesh のすべてのジョイントの集まりです。スケルトンに複数のメッシュのジョイントが何故含まれるのかよくわからない場合は、これに関して詳しい説明をしている Wes Bunn の動画 をご覧ください。
アンリアル エンジン 4 (UE4) では、スケルトンが持つすべてのボーンに対して USkeleton アセットに参照ポーズ (USkeleton.ReferenceSkeleton) があります。Skeleton Bone Index は、こうしたボーンのリストに対するインデックスです。各 UAnimSequence 内に、内部トラック インデックスと対応する Skeleton Bone Index とのマッピングが格納されています。適切なアニメーションを対応するボーンに適用するための評価中にこれを使用しますが、その結果として、スケルトンが変更されると、アニメーションでこのデータを更新しなくてはなりません。そのためアニメーションがダーティとしてマークされ、スケルトンに変更が加えられると再保存する必要があります。
当初の計画は、アニメーション データを抽出し評価するためだけに USkeleton を使用し、それを適切な SkeletalMesh コンポーネントにリターゲットするというものでした。これは素晴らしいアイデアのように思えますが、うまくいきませんでした。SkeletalControls で作業する場合には正確なメッシュ データが必要だからです。例えば、IK を使用する場合、複数のメッシュを組み合わせます。つまり、スケルトンのボーン トランスフォームは全く意味がありません。
スケルトンの参照ポーズのボーン トランスフォームは、アニメーションをスケーリングしたリターゲットにのみ使用します。
ペルソナでスケルトン ウィンドウを開くと以下のようになります。
デフォルトではすべてのボーンを表示します。これらは USkeleton のボーンです。“Show Mesh Bones” に変更すると、現在のプレビュー メッシュに属するボーンだけが表示されます。
これで、USkeleton ボーン インデックスが USkeletalMesh ボーン インデックスに対応しないことがわかりますが、FCompactPoseBoneIndex はどこにあてはまるのでしょうか?
FCompactPoseBoneIndex はFCompactPose でのみ使用可能なインデックスです。FCompactPose は、ランタイムのボーン セットであり、評価中に使用されます。現在のメッシュの LOD (Level of Detail) に関するジョイントのみを含む連続的なリストです。複数の LOD がある場合、最適化のためにジョイントのサブセットだけを持つことができます。FCompactPose の前には、ブランチが使用されました。しかし、ブランチは予測ミスが原因でパフォーマンスの問題を引き起こしました。FCompactPoseBoneIndex は、ブランチなしでのイタレーションを可能にします。
FCompactPoseBoneIndex は、単に整数です。独自の型である理由はコードをわかりやすくするためです。ある関数が FCompactPoseBoneIndex をとるか、戻せばそのボーン インデックスがどの空間で機能するかが正確にわかります。メッシュやスケルトンのインデックスを使って compact index を使用することを意図した関数を誤って呼び出すようなこと (またはその逆) がなくなります。コンパイラが拒否するからです。
すべてのコア アニメーション コードは FCompactPose を使用します。API を使用してこうした 3 種類のボーン インデックス間を切り替えることができます。 スケルタルメッシュとスケルトンの間の変換 API のほとんどは USkeleton にあります。(FSkeletonToMeshLinkup)
まとめると以下になります。
- Mesh Bone Index - SkeletalMesh コンポーネントの SpaceBases または LocalAtoms で使用される SkeletalMesh Bone Index です。
- Skeleton Bone Index - すべてのスケルタルメッシュのジョイントの集まりが含まれます。基本的にこれはアニメーション トラックのインデックスです。アニメーションは、トラック データのためにこのインデックスを参照します。
- FCompactPoseBoneIndex - これはメッシュの LOD サポートの一部であるすべてのジョイントのサブセットのために使用されます。すなわち、LOD 0 は MeshIndex に一致しますが、LOD 1 または LOD2 になると、サブセットのみを持つことができます。そのため MeshIndex には対応しなくなります。
以下は、CompactPoseBoneIndex からメッシュ ボーンに変換する一例です。
// iterate through all compact pose (すべての compact pose でイタレート)
for (FCompactPoseBoneIndex BoneIndex :Output.Pose.ForEachBoneIndex())
{
// ask mesh pose for the compact pose (compact pose に対して mesh pose を求める)
FMeshPoseBoneIndex MeshPoseBoneIndex = Output.Pose.GetBoneContainer().MakeMeshPoseIndex(BoneIndex);
// assuming local mesh buffer is your buffer for skeletalmesh (ローカルのメッシュのバッファがスケルタルメッシュのバッファであることを前提)
Output.Pose[BoneIndex] = LocalMeshBuffer[MeshPoseBoneIndex.GetInt()];
}
最後になりますが、 USkeleton はそれが何であるかを示すわかりやすい名前になっていません。この動画 をご覧になると USkeleton がどのようなものであるかがわかることでしょう。
この記事が様々なボーン インデックスとその使用方法についてのご理解に役立てば幸いです。