今年もよろしくお願いしますm(_ _)m
さて正月何をしていたかって、DirectX June2010のSkinnedMeshサンプルと格闘していました
人生でこれほど目が痛くなった正月はない…
まったく…Tinyは男なのか女なのか結局わからなかったw
理解はどこら辺まで深まったのかというと、「固定機能インデックスなしスキニングによるレンダリング」は(おそらく)理解できました!
1週間近くかかったorz
「ゲームつくろー!」というありがたいサイト様で何点かわからなかった所が詳しく解説されているのを数日前に発見し、後は何とかソースコードを追う事が出来た…
忘れないようメモしておく
どうやら肝となる構造体は
struct D3DXFRAME_DERIVED : public D3DXFRAMEこいつと
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINERこいつと
class CAllocateHierarchy : public ID3DXAllocateHierarchyこいつらしい
D3DXFRAME_DERIVED は結局
{
//D3DXFRAMEで定義
LPSTR Name; //フレームの名前
D3DXMATRIX TransformationMatrix; //座標変換用行列(自分のフレームの座標系から親のフレームの座標系へ)
LPD3DXMESHCONTAINER pMeshContainer; //上の2番目の構造体::メッシュコンテナへのポインタ
struct _D3DXFRAME *pFrameSibling; //兄弟となるフレームへのポインタ
struct _D3DXFRAME *pFrameFirstChild; //子となるフレームへのポインタ
//D3DXFRAME_DERIVEDで定義
D3DXMATRIXA16 CombinedTransformationMatrix; //自分のフレームの座標系から一気にローカル座標系へ座標変換する行列
}
次にD3DXMESHCONTAINER_DERIVEDは
{
//D3DXMESHCONTAINERで定義
LPSTR Name; //メッシュコンテナの名前
D3DXMESHDATA MeshData; //メッシュのデータ本体(後に最適化されたものが入る)
LPD3DXMATERIAL pMaterials; //マテリアル情報へのポインタ
LPD3DXEFFECTINSTANCE pEffects; //エフェクトへのポインタ
DWORD NumMaterials; //マテリアルの数
DWORD *pAdjacency; //隣接性情報
LPD3DXSKININFO pSkinInfo; //スキンインフォ?何じゃそら
struct _D3DXMESHCONTAINER *pNextMeshContainer; //次のメッシュコンテナへのポインタ(どうやらメッシュコンテナはリストを形成できるみたい)
//D3DXMESHCONTAINER_DERIVEDで定義
LPDIRECT3DTEXTURE9* ppTextures; // array of textures, entries are NULL if no texture specified
// SkinMesh info //↑テクスチャの配列、テクスチャがなければNULLが入ってる
LPD3DXMESH pOrigMesh; //xファイルから読み込んだ最適化前のメッシュへのポインタ
LPD3DXATTRIBUTERANGE pAttributeTable; //取り敢えずは固定機能インデックスなしスキニングでは使わない
DWORD NumAttributeGroups; //上に同じ
DWORD NumInfl; //最大何本のボーンが一つの頂点に影響するか
LPD3DXBUFFER pBoneCombinationBuf; //ボーンコンビネーションテーブル:LPD3DXBONECOMBINATIONを含むバッファ
D3DXMATRIX** ppBoneMatrixPtrs; //ボーン座標からローカル座標へ変換する行列へのポインタ
//上のコンビネーションテーブルにあるボーン番号と配列の並びが対応している
D3DXMATRIX* pBoneOffsetMatrices; //頂点をローカル座標からボーン座標へ一辺に変換する行列の配列(こいつもテーブルのボーン番号と対応済み)
DWORD NumPaletteEntries; //pAttributeTableと同じく取り敢えずパスして大丈夫
bool UseSoftwareVP; //頂点変換をソフトウェアでやるかどうか
DWORD iAttributeSW; //こいつは上の判定に使ってるみたい
}
であとはclass CAllocateHierarchyのCreateFrameとCreateMeshContainerというメンバ関数が定義されてる。
どうもCAllocateHierarchyはフレームの階層構造と、メッシュコンテナを作ったり解放したりするインターフェースらしい。
これ自体は何という事もなく前者は単純にD3DXFRAME_DERIVEDを作成して初期化。後者はD3DXMESHCONTAINER_DERIVEDを作ってファイルから読み取った情報を格納するだけ…
とか思ってたらCreateMeshContainerの最後の方でGenerateSkinnedMeshとかいう関数へ入る
この関数が無駄に長い…。でもやっていることはメッシュを最適化してD3DXMESHCONTAINER_DERIVEDで定義されたスキンメッシュ関係のメンバ変数へ値を代入しているだけ
ここでID3DXSkinInfo::ConvertToBlendedMeshというメッシュの最適化関数が呼ばれていて、コンビネーションテーブルや最大影響数が設定される。
(どうやらID3DXSkinInfoは直訳でのスキンの情報だけじゃなくて、最適化なんかも行ってくれるみたい)
これらは全部、引数に肝心のXファイルのファイル名とこれら三つの構造体とクラス、をとるD3DXLoadMeshHierarchyFromXという関数内で呼ばれるらしい
ここまで来て疑問に思ったのが、ボーンとフレームはどう関係しているのかという事だった。どうもそこら辺があいまいだったので悩みながらコードを読んでると
どうやらボーンの座標系とフレームの座標系は同じもののようだった。というよりかはボーンなんてものは概念にすぎないのかな?
実際にはメッシュコンテナのppBoneMatrixPtrsにある行列へのポインタはそれぞれがフレームのCombinedTransformationMatrixを指しているみたい
フレーム毎に更新される行列はメッシュコンテナに概念としてあるボーンじゃなくてフレームの行列だし…
メッシュコンテナにあるボーンオフセット行列は、つまるところローカル座標から初期姿勢のフレームが持つ座標系へ一気に変換する行列。
こいつはXファイル内部に情報があるようでCreateMeshContainerでID3DXSkinInfo内部に格納されている情報がコピーされる。
描写の処理自体は非常にシンプルで逆に驚いたくらいだった
具体的には各頂点をまずメッシュコンテナにあるpBoneOffsetMatricesでボーン座標へ変換
そのあとppBoneMatrixPtrsの行列ででローカル座標へ戻す…をブレンドしながら繰り返しているだけ
結局このスキンメッシュは
①xファイルのファイル名を指定してD3DXLoadMeshHierarchyFromXを呼ぶ
②内部でCAllocateHierarchy::CreateFrameが呼ばれてフレームの階層構造が作られる
③さらにD3DXLoadMeshHierarchyFromX内部でCAllocateHierarchy::CreateMeshContainerがよばれメッシュコンテナが作成される
④CAllocateHierarchy::CreateMeshContainer内でGenerateSkinnedMesh関数が呼ばれ、メッシュの最適化やスキンメッシュの情報(ボーンが最大何本あるかとか)が
メッシュコンテナに登録される
⑤多分どっかでD3DXFRAMEのpMeshContainerへ作成したメッシュコンテナが登録される
(実際にはこのサンプルではメッシュコンテナは1つしか作成されず、最下層のフレームにのみ登録されている。それ以外の親のフレームはNULLポインタが入っているようだった)
⑥D3DXLoadMeshHierarchyFromX終了
⑦SetupBoneMatrixPointersが呼ばれる。こいつはフレームのCombinedTransformationMatrixのポインタをメッシュコンテナのppBoneMatrixPtrsのへ登録し
メッシュコンテナ内のコンビネーションテーブル内のボーン番号と、「ボーン座標(つまるところフレームが持つ座標系)からローカル座標への変換行列」が対応するようにする関数。
ここまででセットアップが終わる。
後はフレーム毎に
⑧フレーム内部の座標系の相対位置を変化させる(つまりボーンを動かす)
描写毎に
⑨ボーンオフセット行列pBoneOffsetMatricesで、初期のローカル座標系での頂点座標から初期姿勢のボーン座標系へ頂点座標を変換。
そのあとppBoneMatrixPtrsで現在の姿勢でのローカル座標系へ変換これを頂点ごとに影響するボーンの数だけ繰り返す
を行っている。
…よくできていると言えば良くできているが…誰だこんなの考えたのwもうちょっと何とかならんものか・・・
今回見てわかったのは、いままでボーンを動かすという表現に惑わされてボーンという実態があるかのように感じていたが、実はそれはフレームを動かすということだった事。
実際のところこれはスキンメッシュでも、そうでない機械のような階層のあるメッシュの集合体もあんまり変わんないという事がわかった…。
たぶんこのサンプルのpMeshContainerへパーツごとにメッシュを登録して、描写の処理をちょっと工夫すれば戦車とかは作れそうだ。
・・・!!!書いてて閃いたのだが、メッシュコンテナがリストを形成する事はもしかしてバウンディングボックスとかは、ここへ格納したら良かったりしないか!?とか思ってみる。これはもうちょっと調べる必要がありそうだけど…