階層有り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 );
};