階層型メッシュを用いるオブジェクトを複数表示

プロペラを回すとか、腕を動かすとか、メッシュを部分的にアニメーションさせたいときには階層型メッシュを使います。要はD3DXLoadMeshHierarchyFromX()で読み込むやつのことで、階層関係を持つ「フレーム」にパーツごとのメッシュが格納されています。
また、スキンメッシュというのもこれに属するもので、ちょっと特殊なのは各フレームが各々メッシュを持っているわけではありません。メッシュは一つだけしかない(二つ以上のこともある)けど、それを各フレームからの影響に応じて変形させることで部分的なアニメーションを行う仕組みで、このときのフレームを「ボーン」と呼んだりします。

で、この階層型メッシュは複数のオブジェクトから同じものを使うなら当然使いまわすリソースです。このときに、どの情報がメッシュに共通で、どの情報が各オブジェクトごとに持つものなのかという話です。
わかりづらい用語の定義の見直しもかねてまとめてみました。

ワールド変換行列

個々のオブジェクトの全身のワールド座標での位置と向きとスケールを定義する。
これはもちろんオブジェクトごとに持つ情報。

フレーム行列

階層型メッシュの各フレームの姿勢を定義する行列。砲身がどの角度向いてるとか、腕が曲がってるとか。スキンメッシュの場合はボーン行列という。D3DXFRAME構造体に入っているTransformationMatrixがそれ。アニメーションコントローラのAdvanceTime()関数を呼ぶことでDirectXによって計算される。
これはD3DXFRAME構造体は読み込み時に確保されるものなのでメッシュごとに共有される。したがってオブジェクトごとの処理のときに毎回再計算しなければならない。例えば一つのオブジェクトでAdvanceTime()して、次のオブジェクトではアニメーションを進めないのでAdvanceTime()を呼ばないとかすると、フレーム行列が計算されず前のオブジェクトの姿勢になってしまう。したがってアニメーションを進めない場合でもAdvanceTime(0)は呼ぶようにする。

フレーム合成行列

サンプルや本のほとんどではD3DXFRAMEから派生した構造体にCombinedTransformationMatrixとかそういう名前で保持されている。これはいわば各フレームごとの実際のワールド変換行列にあたる。一番親のルートフレームの場合は最初のワールド変換行列そのものに自身のフレーム行列をかけたもの、そしてその子フレームの場合はそれにさらに子自身のフレーム行列をかけたもの・・・と子に行くにつれてフレーム行列が掛け合わされていって構成される。スキンメッシュの場合はボーン合成行列と呼ばれる。
今回のポイントはここ。
毎フレーム(時間ステップのフレームの方)、オブジェクトごとにワールド変換行列が決定し、AdvanceTime()によってフレーム行列が計算され、それらからフレーム合成行列を計算する。もしフレーム合成行列を使うのが描画だけならば、更新フェイズにはワールド変換行列とフレーム行列の更新のみを行い、描画フェイズにフレーム合成行列を計算して描画するというのが一つの方法だ。しかしこうすると、描画時に最初に参照するフレーム行列が最後にAdvanceTime()を呼ばれた最後のオブジェクトのフレーム行列になってしまっている。そこでオブジェクトごとにまたAdvanceTime(0)を呼んで自身のフレーム行列を計算しなおさなくてはならない。
そこで、更新フェイズの時にフレーム合成行列も計算してしまい、それをオブジェクトごとの情報として保持しておくという方法が考えられる。この方法を使うもう一つの理由として、フレーム合成行列はフレームごとの衝突判定を行う時にも必要となる情報であるということがある。AとBの衝突判定を行うとき、BにBの行列とAの逆行列を掛けることでBをAのローカル空間に移せるからである。衝突判定は更新フェイズに行うものなので、結局この段階でフレーム合成行列を計算することになるっぽい。

オフセット合成行列

スキンメッシュの場合はさらにボーンごとの初期姿勢として「ボーンオフセット行列」というものが存在する。pMeshContainer->pSkinInfo->GetBoneOffsetMatrix()で取得できるもので、これもフレームかメッシュコンテナの派生構造体に格納しておく。そして上のボーン合成行列にさらにこのオフセット行列を掛けたオフセット合成行列を用いてワールド変換を行う。情報としての扱いはフレーム合成行列と同じ。

アニメーションコントローラ

アニメーションがどこまで進んでいるか等はオブジェクト独自の情報である。メッシュ読み込み時に取得できるオリジナルのアニメーションコントローラからCloneAnimationController()でコピーを作って各オブジェクトに保持しておけばいい。


という感じでしょうか。
いろいろややこしいのでこうやってまとめとかないとクラス設計が全然進みません。