階層有りXFile描画
-読み込み、更新、描画-

■バージョン

	DirectX
		DirectX9

	VisualStudio
		VisualStudio 2015

■サンプル

以下のリンクでサンプルをダウンロードできます。
※サンプルはアニメーション処理も入っています。

サンプル

■概要

このページでは階層有りXFileの読み込み、更新、描画を行います。
準備のページを見ていない人はそちらのページの知識、準備が
整っていることを前提として書いていますので、
以下のリンクから確認をお願いします。

階層有りXFile描画 -準備-

■読み込み

階層有りのXFileの読み込みには
「D3DXLoadMeshHierarchyFromX」を使用します。
この関数の第四引数に「ID3DXAllocateHierarchy」を
継承したクラス、または構造体を指定します。
この時に「CreateFrame」「CreateMeshContainer」が
自動で呼び出されます。
※サンプルのHierarchyXFileクラスのLoad関数が読み込み処理の部分です。

●D3DXLoadMeshHierarchyFromX
	戻り値:

	引数:
		LPCTSTR Filename:
			読み込むファイルの名前

		DWORD MeshOptions:
			メッシュの作成オプション

		LPDIRECT3DDEVICE9 pDevice:
			IDirect3DDevice9インターフェイスのポインタ

		LPD3DXALLOCATEHIERARCHY pAlloc:
			LPD3DXALLOCATEHIERARCHYインターフェイスのポインタ
			※「CreateFrame」などの継承を行った
			 データ構造をを用意する必要あり

		LPD3DXLOADUSERDATA pUserDataLoader:
			ID3DXLoadUserDataインターフェース
			※XFileにユーザーの作成した定義があるときに使用

		LPD3DXFRAME* ppFrameHeirarchy:
			ルートフレームを格納するためのLPD3DXFRAMEのポインタ
			このデータからフレーム階層を全て把握できる

		LPD3DXANIMATIONCONTROLLER* ppAnimControlle:
			ID3DXAnimationControllerインターフェイスのポインタ
			アニメーションとフレームは別々に扱われるので、
			アニメーションに関するデータは
			このID3DXAnimationControllerで使用、確認する

	内容:
		階層有りのXFileを読み込んで分解してくれます。
		分解されたフレーム、メッシュコンテナは
		第三引数の変数の「CreateFrame」「CreateMeshContainer」を
		呼び出して、そこで詳細なデータの初期化を行います。

●注意点
	「CreateFrame」「CreateHierarchy」内で不正アクセスなどのエラーが
	行った場合、各関数ではなく、別の箇所で停止しているように見えることがあります。
	D3DXLoadMeshHierarchyFromXで失敗、エラーになった場合は
	「CreateFrame」「CreateMeshContainer」も合わせて疑ってください。

■更新

各階層のフレームはルートの行列に変化が起こるたびに更新を行う必要があります。
この更新はDirectXが用意した関数を使用して行うのではなく、
開発側で実装をしなくてはいけません。
※サンプルのHierarchyXFileクラスのUpdateFrame関数が更新処理の部分です。

●フレームの遷移方法
	フレームを更新するには全てのフレームに対してアクセスする必要があり、
	そのための方法として使用されているのが、
	ルートフレームの情報を使用した再帰呼び出しです。
	ルートフレームの中には兄弟フレーム、子フレームの情報が格納されている
	「pFrameSibling」「pFrameFirstChild」があります。
	これらの変数に値が格納されていれば、その値を使用して更新関数を呼び出します。
	こうすることで、ルートから始まり、再帰を繰り返すことで
	フレームの末端まで更新処理が行き届くことになります。

	・兄弟再帰
		// 兄弟があれば再帰で呼び出す
		if (frame->pFrameSibling != NULL)
		{
			// 兄弟には親の行列を渡す
			UpdateFrame(frame->pFrameSibling, parent_matrix);
		}

	・子再帰
		if (frame->pFrameFirstChild != NULL)
		{
			// 子供には自分の行列を渡す
			UpdateFrame(frame->pFrameFirstChild,
				&frame->CombinedTransformationMatrix);
		}

■描画


●再帰
	階層有りのXFileの描画は各フレームごとのメッシュコンテナ毎に
	描画をする必要があります。
	そのため、更新の時と同じようにルートから各フレームを遷移する
	再帰関数を作成し、その中でそのフレームが持っている
	メッシュコンテナを描画させます。
	
	例:
		FrameData *frame_data = (FrameData*)frame;
		LPD3DXMESHCONTAINER container_data = frame_data->pMeshContainer;

		// コンテナの数だけ描画する
		while (container_data != NULL)
		{
			DrawMeshContainer(frame, container_data);

			container_data = container_data->pNextMeshContainer;
		}

●メッシュコンテナの描画
	メッシュコンテナの描画は通常のXFileの描画同じで、
	マテリアルの数だけDrawSubsetを呼び出します。
	注意点があるとしたら更新で作成した各フレーム毎の行列を
	きちんと設定する必要があります。

	例:
		FrameData *frame_data = (FrameData*)frame;
		MeshContainer *mesh_container = (MeshContainer*)container;

		// 描画位置行列の設定
		g_pD3DDevice->SetTransform(
				D3DTS_WORLD,
				&frame_data->CombinedTransformationMatrix);

		// メッシュの描画
		for (int i = 0; i < mesh_container->NumMaterials; i++)
		{
			g_pD3DDevice->SetMaterial(&mesh_container->pMaterials[i].MatD3D);
			g_pD3DDevice->SetTexture(0, mesh_container->m_TextureList[i]);
			mesh_container->MeshData.pMesh->DrawSubset(i);
		}