階層有りXFile描画
-準備-

■バージョン

	DirectX
		DirectX9

	VisualStudio
		VisualStudio 2015

■サンプル

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

	サンプル

■概要

このページでは階層有りXFileの概要説明と
読み込みを行うまでの準備、知識についての説明を行っています。

■階層有りXFile

XFileはフレームと呼ばれる階層構造を使用して
メッシュデータに親子関係を持たせることができます。
この階層有りのデータを読み込む場合は
通常のXFile読み込みとは別の方法が必要です。
※通常の方法でも読み込めますが、階層情報は入っていません。

●階層付きXFile詳細
	以下のリンクで階層有りXFileの説明を行っています。
	
	3Dモデル -階層-

■フレームとメッシュコンテナ

階層つきのXFileでは「フレーム」と「メッシュコンテナ」という
2つのデータ構造を使用します。

●フレーム(Frame)
	フレーム(Frame)はXFileで設定されているフレームのデータです。
	データを格納するために「_D3DXFRAME」構造体が用意されています。

	・typedef
		D3DXFRAME
		*LPD3DXFRAME

	・メンバ変数
		・LPSTR Name
			フレームの名前

		・D3DXMATRIX TransformationMatrix
			トランスフォーム行列

		・LPD3DXMESHCONTAINER pMeshContainer
			メッシュコンテナーのポインター

		・D3DXFRAME *pFrameSibling
			兄弟フレームのポインタ

		・D3DXFRAME *pFrameFirstChild
			子フレームのポインタ

	・継承
		このままでも使用可能ですが、各フレームの描画用の行列などを
		保存するメンバを追加するために、この構造体を継承して
		新しい構造体を作成する方法がよく使用されています。

		例:
			・strcut
				typedef strcut frame_data__ : public D3DXFRAME
				{
					// フレーム描画用の行列
					D3DXMATRIX m_FrameDrawMatrix;
				} FrameData;

			・class 
				class FrameData : public D3DXFRAME
				{
				private:
					// フレーム描画用の行列
					D3DXMATRIX m_FrameDrawMatrix;
				};

●メッシュコンテナ
	メッシュコンテナ(MeshContainer)はフレーム内で設定されている
	メッシュやマテリアルをまとめたデータです。
	データを格納するために_D3DXMESHCONTAINER構造体が用意されています。

	・typedef
		D3DXMESHCONTAINER
		*LPD3DXMESHCONTAINER

	・メンバ変数
		LPSTR Name
			メッシュの名前

		D3DXMESHDATA MeshData
			メッシュデータ

		LPD3DXMATERIAL pMaterials
			マテリアル配列

		LPD3DXEFFECTINSTANCE pEffects
			エフェクトインターフェースのポインタ
			※エフェクトはシェーダファイルと考えて問題ありません。

		DWORD NumMaterials
			メッシュで使用しているマテリアルの数

		DWORD *pAdjacency
			隣接ポリゴンデータ

		LPD3DXSKININFO pSkinInfo
			スキンインターフェースのポインタ

		struct _D3DXMESHCONTAINER *pNextMeshContainer
			次のメッシュコンテナのポインタ

	・継承
		メッシュコンテナもフレーム同様にD3DXMESHCONTAINERを
		継承したクラス、または構造体を用意し、
		そこに各プログラムで必要な情報を追加して使用されています。
		よく見かけるのはこのメッシュのマテリアルで使用している
		テクスチャのポインタを格納できる配列です。

		例:
			・struct
				typedef struct mesh_container__ : public D3DXMESHCONTAINER
				{
					// マテリアルで使用されているテクスチャ格納用
					LPDIRECT3DTEXTURE9 *m_TextureList;
				} MeshContainerData;

			・class
				class MeshContainerData : public D3DXMESHCONTAINER
				{
				private:
					// マテリアルで使用されているテクスチャ格納用
					LPDIRECT3DTEXTURE9 *m_TextureList;
				};

■読み込み準備

DirectX9で階層有りのXFileを読み込む場合、パーサー関数として
「D3DXLoadMeshHierarchyFromX」が用意されていますが
D3DXLoadMeshHierarchyFromXを使用するには
「ID3DXAllocateHierarchy」を継承したクラス、
または構造体を用意する必要があります。

●D3DXLoadMeshHierarchyFromX
	D3DXLoadMeshHierarchyFromXはXFileデータを
	解析してくれますが、その解析されたデータを使用した
	フレーム、メッシュコンテナの作成と削除は
	自作する必要があります。
	その作成、削除の関数がID3DXAllocateHierarchyに
	用意されています。

●ID3DXAllocateHierarchy
	ID3DXAllocateHierarchyはインターフェースで
	D3DXLoadMeshHierarchyFromXによって
	解析されたファイルデータのフレームとメッシュコンテナを
	自作するための純粋仮想関数が定義されています。
	以下は定義されている関数です。

		・CreateFrame
		・CreateMeshContainer
		・DestroyFrame
		・DestroyMeshContainer

●CreateFrame
	CreateFrameは解析されたXファイルのフレーム情報から
	このアプリ内で使用するフレームデータを作成するための関数で、
	D3DXLoadMeshHierarchyFromX呼び出しの際に自動で呼び出されます。

	・宣言
		宣言はID3DXAllocateHierarchy内に宣言が記述されているので
		そちらを使用します。
		※ID3DXAllocateHierarchyに書かれている「PURE」は「= 0」の
		 マクロなので宣言では外します。

		STDMETHOD(CreateFrame)(
			THIS_ LPCSTR Name, 
			LPD3DXFRAME *ppNewFrame);

			戻り値:
				HRESULT:
					成功:S_OK
					失敗:S_OK以外

			引数:
				THIS_ LPCSTR Name:
					フレームの名前
					※THIS_マクロは空白

				LPD3DXFRAME *ppNewFrame:
					作成フレームの出力用

			内容:
				フレームの作成を行い初期化します。
				この時に作成するフレームは自作フレームで
				作成し、必要なデータを設定します。

	・定義
		定義は自作フレームにどのようなデータを持たせているかで
		内容は異なりますが、共通処理はフレームの名前を
		コピーする部分です。

		例:
			
			// 名前のコピー
			frame->Name = new char[lstrlenA(name) + 1];
			strcpy_s(frame->Name, lstrlenA(name) + 1, name);
			
●CreateMeshContainer
	CreateMeshContainerは解析されたXファイルの
	メッシュコンテナ情報からこのアプリ内で使用する
	メッシュコンテナデータを作成するための関数で、
	D3DXLoadMeshHierarchyFromX呼び出しの際に自動で呼び出されます。

	・宣言
		宣言はID3DXAllocateHierarchy内に宣言が記述されているので
		そちらを使用します。
		※ID3DXAllocateHierarchyに書かれている「PURE」は「= 0」の
		 マクロなので宣言では外します。

		STDMETHOD(CreateMeshContainer)(
			THIS_ LPCSTR Name, 
			CONST D3DXMESHDATA *pMeshData, 
			CONST D3DXMATERIAL *pMaterials, 
			CONST D3DXEFFECTINSTANCE *pEffectInstances, 
			DWORD NumMaterials, 
			CONST DWORD *pAdjacency, 
			LPD3DXSKININFO pSkinInfo, 
			LPD3DXMESHCONTAINER *ppNewMeshContainer);

			戻り値:
				HRESULT:
					成功:S_OK
					失敗:S_OK以外

			引数:
				THIS_ LPCSTR Name:
					メッシュの名前
					※THIS_マクロは空白

				CONST D3DXMESHDATA *pMeshData,:
					メッシュ情報

				CONST D3DXMATERIAL *pMaterials:
					マテリアル情報

				CONST D3DXEFFECTINSTANCE *pEffectInstances:
					エフェクト(シェーダ)情報

				DWORD NumMaterials:
					マテリアルの数

				CONST DWORD *pAdjacency:
					隣接ポリゴン番号のリスト

				LPD3DXSKININFO pSkinInfo:
					スキン情報

				LPD3DXMESHCONTAINER *ppNewMeshContainer:
					出力用のメッシュコンテナ

			内容:
				メッシュコンテナの作成を行い初期化します。
				この時に作成するメッシュコンテナは
				自作コンテナで作成し、必要なデータを設定します。

	・定義
		定義はフレームと同じでどのようなデータを持たせているかで
		内容は異なりますが、引数を使用した共通処理の数が
		フレームとは違い多いことが特徴です。

		・メッシュ名のコピー
			メッシュ名をコピーします。

			例:
			
				// 名前のコピー
				container->Name = new char[lstrlenA(name) + 1];
				strcpy_s(container->Name, lstrlenA(name) + 1, name);
			

		・メッシュのコピー
			メッシュデータをコピーをします。
		
			例:
			
				// メッシュのコピー
				container->MeshData.pMesh = mesh;
				container->MeshData.Type = D3DXMESHTYPE_MESH;
				// 参照カウンタを加算する
				mesh->AddRef();
			

		・隣接ポリゴン番号のコピー
			隣接ポリゴン番号をコピーします。

			隣接ポリゴン番号:
				隣接ポリゴン番号とは1つのポリゴンに隣接している
				ポリゴンの番号のことです。

			例:
			
				// 隣接ポリゴン番号のコピー
				container->pAdjacency = new DWORD[porigon_num * 3];
				memcpy(container->pAdjacency, adjacency, sizeof(DWORD) * porigon_num * 3);
			



		・マテリアルのコピー
			マテリアルデータをコピーします。
			この時にマテリアルで使用しているテクスチャの読み込みも
			合わせて行います。

			例:
				
				// マテリアルのコピー
				container->NumMaterials = material_num;
				if (material_num > 0)
				{
					container->pMaterials = new D3DXMATERIAL[container->NumMaterials];
					container->m_TextureList = new LPDIRECT3DTEXTURE9[container->NumMaterials];
					memcpy(container->pMaterials, material_data, sizeof(D3DXMATERIAL) * material_num);

					// ファイル読み込み
					for (int i = 0; i < material_num; i++)
					{
						if (container->pMaterials[i].pTextureFilename != NULL &&
							g_TextureList[container->pMaterials[i].pTextureFilename] != NULL)
						{
							LPDIRECT3DTEXTURE9 texture;

							if (FAILED(D3DXCreateTextureFromFileA(
								device,
								container->pMaterials[i].pTextureFilename,
								&texture)))
							{
								g_TextureList[container->pMaterials[i].pTextureFilename] = texture;
								container->m_TextureList[i] = texture;
							}
						} else {
							container->m_TextureList[i] = NULL;
						}
					}
				} else {
					// マテリアルの数が0だったら時の仮のマテリアル追加
					container->NumMaterials = 1;
					container->pMaterials = new D3DXMATERIAL[container->NumMaterials];
					container->m_TextureList = NULL;
					container->pMaterials[0].pTextureFilename = NULL;
					memset(&container->pMaterials[0].MatD3D, 0, sizeof(D3DMATERIAL9));
					container->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
					container->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
					container->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
					container->pMaterials[0].MatD3D.Diffuse.a = 1.0f;
					container->pMaterials[0].MatD3D.Ambient = container->pMaterials[0].MatD3D.Diffuse;
				}
				

		・データがないケース
			メッシュに法線がなかったり、マテリアルがなかったりする場合
			この関数内でデータの追加を行うことがあります。
			今回のサンプルではマテリアルがなかった場合の対応として
			デフォルトのマテリアルを設定しています。

●DestroyFrame
	DestroyFrameはCreateFrameで作成されたフレームを削除するための関数です。
	「CreateFrame」「CreateMeshContainer」とは違い
	自動で呼び出されないので、自分で呼び出す必要があります。

	・宣言
		宣言はID3DXAllocateHierarchy内に宣言が記述されているので
		そちらを使用します。
		※ID3DXAllocateHierarchyに書かれている「PURE」は「= 0」の
		 マクロなので宣言では外します。

		STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree); 

			戻り値:
				HRESULT:
					成功:S_OK
					失敗:S_OK以外

			引数:
				THIS_ LPD3DXFRAME pFrameToFree:
					削除対象のフレーム

			内容:
				フレームの削除を行います。
				CreateFrame関数で動的に確保したメモリを解放をしつつ
				フレームが保持しているメッシュコンテナを
				削除関数を呼び出します。

	・定義
		関数内ではCreateFrameで動的に確保したメモリを解放します。
		例えばフレーム名はCreateFrameでメモリ確保を行っていますので、
		対象となります。

●DestroyMeshContainer
	DestroyMeshContainerはCreateMeshContainerで作成された
	メッシュコンテナを削除するための関数です。
	「CreateFrame」「CreateMeshContainer」とは違い
	自動で呼び出されないので、自分で呼び出す必要があります。

	・宣言
		宣言はID3DXAllocateHierarchy内に宣言が記述されているので
		そちらを使用します。
		※ID3DXAllocateHierarchyに書かれている「PURE」は「= 0」の
		 マクロなので宣言では外します。

		STDMETHOD(DestroyMeshContainer)(
			THIS_ LPD3DXMESHCONTAINER pMeshContainerToFree);

			戻り値:
				HRESULT:
					成功:S_OK
					失敗:S_OK以外

			引数:
				THIS_ LPD3DXMESHCONTAINER pMeshContainerToFree:
					削除対象のメッシュコンテナ

			内容:
				メッシュコンテナの削除を行います。
				CreateMeshConatainer関数で動的に確保した
				メモリを解放することがメインの処理となります。

	・定義
		関数内ではCreateMeshContainerで確保した動的メモリの解放を行います。
		CreateMeshContainerで確保するメモリは種類が多いので
		忘れないようにする必要があります。

●継承したクラスの例
	// 階層クラス(ID3DXAllocateHierarchyで使う仮想関数のみ定義)
	class HierarchyData : public ID3DXAllocateHierarchy
	{
	public:
		HierarchyData() {}
		
		STDMETHOD(CreateFrame)(THIS_ LPCSTR, LPD3DXFRAME *);
		STDMETHOD(CreateMeshContainer)(
						THIS_ LPCSTR, 
						CONST D3DXMESHDATA *, 
						CONST D3DXMATERIAL *, 
						CONST D3DXEFFECTINSTANCE *, 
						DWORD, CONST DWORD *, 
						LPD3DXSKININFO, 
						LPD3DXMESHCONTAINER *);
	 	STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME );
	 	STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER );
	};