DirectX

【DirectX】cmoファイルを使ってスキンアニメーションさせるまで

More than 1 year has passed since last update.

cmoファイルを使ってスキンアニメーションをするために問題となったことの覚書。

DirectX11でサクっとモデルを出す手としてDirectXTKのcmoファイルのローダーを使用するという手があります。
http://masafumi.cocolog-nifty.com/masafumis_diary/2013/02/windows-storefb.html

これでスタティックモデルの表示はサクッとできるのですが、スキンウェイトが設定されているモデルを表示しようとするとクラッシュしてしまいます。

というのも、どうもDirectXTKのcmoファイルのローダーはスキンアニメーションが未実装のようで、コードを調べるとこんなコードが。

#if 0
        // Animation data
        if ( *bSkeleton )
        {
      省略
           ・
      ・
      ・
        }
#else
        UNREFERENCED_PARAMETER(bSkeleton);
#endif

#if 0で無効になっているコードがスケルトン、スキンウェイト、アニメーションクリップといわれるアニメーション用のデータをロードしている箇所なので、まずこのコードを有効にする必要があります。そうするとスキンが設定されているモデルをロードしてもクラッシュせずに表示できるようになります。しかしアニメーションはできません。
もう少しコードを調べていくと、有効にしたコードにこんなTODOコメントがあります。

for( UINT j = 0; j < *nBones; ++j )
{
    // Bone name
    nName = reinterpret_cast<const UINT*>( meshData + usedSize );
    usedSize += sizeof(UINT);
    if ( dataSize < usedSize )
        throw std::exception("End of file");

    auto boneName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

    usedSize += sizeof(wchar_t)*(*nName);
    if ( dataSize < usedSize )
        throw std::exception("End of file");

    // TODO - What to do with bone name?
    boneName;

    // Bone settings
    auto bones = reinterpret_cast<const VSD3DStarter::Bone*>( meshData + usedSize );
    usedSize += sizeof(VSD3DStarter::Bone);
    if ( dataSize < usedSize )  
        throw std::exception("End of file");

    // TODO - What to do with bone data?
    bones;
}

ボーンの名前見つけたけどどうする?ボーンのデータ見つけたけどどうする?的な意味でしょうか。他にもアニメーションクリップ見つけたけどどうする?キーフレーム見つけたけどどうする?的なコメントもあります。
独自で実装してねってことなのかな?ってことで実装してみましたが、こんな感じでうまくアニメーションしない。

anim0.gif

グラフィック診断を使って調べてみたところ頂点に設定されているボーンインデックスやスキンウェイトがおかしくなっている模様。そこで、ローダーのスキンウェイトを頂点バッファに設定しているコードを調べてみると、下記のようなコードになってました。

skinv->SetBlendIndices( *reinterpret_cast<const XMUINT4*>( skinptr->boneIndex ) );
skinv->SetBlendWeights( *reinterpret_cast<const XMFLOAT4*>( skinptr->boneWeight ) );

全部の頂点に0番目の頂点のウェイトを設定してしまうコードになってます。これを次のように修正するとスキンの設定は修正できます。

skinv->SetBlendIndices( *reinterpret_cast<const XMUINT4*>( skinptr[v].boneIndex ) );
skinv->SetBlendWeights( *reinterpret_cast<const XMFLOAT4*>( skinptr[v].boneWeight ) );

ここまでの実装ができるとこんな感じ。

ezgif.com-resize (1).gif

これでもまだおかしいのですが、これはfbxからcmoファイルへの変換のコンバーターの問題で、スケルトンの情報をちゃんと出力してくれていないためです。コンバーターのソースは公開されていないのでカスタマイズすることはできないので、スケルトンの情報だけ別途出力するスクリプトなりプラグインなりを自作する必要がありそうです。

というわけで、cmoファイルを使ってスキンアニメーションさせるまでに発生した問題の覚書でした。今時独自に3Dモデルフォーマットを考えて3Dモデルを表示したいなんて、酔狂な人はそんなにいないかと思いますが、cmoファイル自体は非常にシンプルなフォーマットで分かりやすかったので、3Dモデルフォーマットについて勉強してみたいって人は遊んでみるといいんじゃないでしょうか。