【Csharp】XNAのスキンメッシュアニメーションのからくり。

XNAのスキンメッシュアニメーションには、コンテンツパイプラインの理解が不可欠だということを以前書いた。

というわけで、実際に動いているサンプルプログラムのコンテンツパイプライン拡張部分のソースを読んでみた。

ソース元は書籍『XNA 3D-CGプログラミング』(I/O BOOKS)の付録なんだけれども、コンテンツパイプライン部分はおそらく公式が出しているスキンメッシュのサンプルプログラムだと思う。

つまり、ソース元のさらにソース元がこちらと思われる。

http://xbox.create.msdn.com/ja-JP/education/catalog/sample/skinned_model

こちらのサンプルのほうが、コメントも日本語化されていて、読みやすいことに気がついたが、まあそれはよい。

XNAのコンテンツパイプラインから、モデルとアニメーションを読み込む場合、まず①インポーターでアセットを解析して、②プロセッサーで動作をカスタマイズ、その後もなんやかやあるみたいだが、まあたいていは①と②のどちらかをカスタマイズすることになる。

まずインポーターは、独自の3DCGファイルのプロトコルを作成でもしない限りは、基本的にサードパーティが出したものを使うことになる。(それがない場合は自力でやるしかないが)

そして、FBXとXファイルのインポーターについては、すでにXNAに含まれている。

なので、この例では、対象アセットがFBXなので、インポーターの実装はやっていない。

プロセッサーのみを拡張して、FBXのスキンメッシュアニメーションが出来るようにしている。

スキンメッシュアニメーションのプロセッサーもXNAに含めてくれればいいのに、と思うかもしれないけれども、よくよく考えてみると、実際にスキンメッシュアニメーションを使用するシーンでは、IKを考慮するか、とか、物理挙動も含めるか、とか、可動域を制限するか、とかとか、ゲームの要件によって最終的なポーズを算出するロジックは変わってくる。

おそらく、そのために含められなかったのではないかと推測される。

それはさておき、サンプルではこのプロセッサーの拡張で何をやっているかというと、概略するとこういうことをやっているようだ。

1.スキンメッシュやボーンの構造が正しく読み込まれているかを検証する
2.扱いやすいようにモデル毎のローカル座標系をボーンに焼き付けて、座標系を統一する(たぶんそんな感じ)
3.スケルトンからボーンのフラットな一次元配列を取得して外部からアクセスできるようにする
4.すでにインポータで読み込まれたアニメーションデータ(XNA独自形式)から自作したアニメーションデータへ変換してModelContentのTagに保存する。
5.ModelMesh.Draw()時に使用されるエフェクト(シェーダ)をスキンメッシュ用に用意したFXファイルに差し替える。

前提として、インポータですでにFBXファイルからメッシュデータやアニメーションデータが読み込まれて、XNAの独自のDOMモデルに変換済みとなっている。

つまり、インポータで読み込む段階ですでにエラーが出ているようなFBXファイルは、インポータを改良するか、FBXファイル側を調整するしかない。

何のためにこんなことをしているかというと、ゲーム側のロジックでアニメーションデータを取り出せるようにしているだけのようである。

結局、ボーンやメッシュの座標計算はゲーム側のロジックでやっていて、計算結果の座標を5で差し替えたFXファイルに渡して、画面に表示していたのだ。

あくまで俺の観点だけれども、このスキンメッシュアニメーションの肝は、ゲームロジック側の以下のコードと、差し替えたFXファイルの内容にある。

protected override void Draw(GameTime gameTime) {
    //途中略

    //あるフレームにおける、スケルトンの各ボーンのポーズを計算(わかり易く改変している)
    Matrix[] bones = UpdateSkeltonBones(gameTime);

    foreach (ModelMesh mesh in battery.Meshes)
    {
        foreach (Effect effect in mesh.Effects)
        {
            //計算で求まったポーズのトランスフォームをスキンメッシュ用のFXファイルに送る。
            //スキンに当たる頂点の計算は全てFXファイル側で行う。
            effect.Parameters["Bones"].SetValue(bones);
            effect.Parameters["View"].SetValue(view);
            effect.Parameters["Projection"].SetValue(projection);
        }
        mesh.Draw();
    }

    //以下略

たとえば、アニメーションデータだけ別のファイルから読んだものを差し替えたい(いわゆるリターゲティング)、なんていう場合、4のアニメーションデータを読み捨てて、独自にアニメーションデータ(フレーム毎のトランスフォーム情報)を読み込んでおき、フレームのポーズをゲームロジックで計算して渡せばよいことになる。はず。

というわけで、次回は簡単な関節を持つモデルのFBXファイルに対して、別のルートで読み込んだアニメーションデータを適用する(いわゆるリターゲティング)ということをやってみようと思う。

そして、それが出来れば、ひとまず、XNAにおけるスキンメッシュアニメーションの検証は終了としたいと思う。

4年前

コメントを残す

メールアドレスが公開されることはありません。