#pragma once
#include "d3dx9anim.h"

/// メッシュの頂点データ
struct Vertex {
	D3DXVECTOR3 position;	//頂点位置
//	D3DXVECTOR3 normal;		//法線ベクトル
};

/// メッシュのポリゴンデータ
struct Face {
	unsigned short indices[3];		// 3頂点のインデックス
	unsigned long material_number;	// 材料番号
};

// メッシュデータ保存用構造体
struct MeshData {
	vector<Vertex> vertices;
	vector<Face> faces;
	vector<D3DMATERIAL9> material;
	vector<string> texture_filename;
};

/// メッシュのベース
class Mesh {
protected:
	LPDIRECT3DDEVICE9 pDevice;			// Direct3Dデバイスオブジェクト
	LPD3DXMESH pMesh;					// メッシュ
	D3DMATERIAL9* pMeshMaterials;		// マテリアル配列
	LPDIRECT3DTEXTURE9*	pMeshTextures;	// テクスチャ配列
	DWORD dwNumMaterials;				// マテリアル・テクスチャ配列の大きさ
	void SetMesh(MeshData meshData);	// MeshDataをグラフィックボードのメモリにセット
public:
	Mesh(LPDIRECT3DDEVICE9 pDevice);
	virtual ~Mesh();
	virtual void Draw(D3DXVECTOR3 position, D3DXMATRIX rotation);
	virtual LPD3DXMESH GetMesh();
	virtual int GetNumMaterial();
};

/// Xファイルから読込んだメッシュ
class XFileMesh sealed : public Mesh {
public:
	XFileMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDev);
};

/// メタセコイアファイルから読込んだメッシュ
class MqoMesh sealed : public Mesh {
private:
	template <typename T> vector<T> MaterialPickOut(string str, char* name, int n); // マテリアル行の文字列から指定した名前の値の配列を取得
	void LoadMaterial(ifstream& ifs, MeshData& meshData);
	void LoadObject(ifstream& ifs, MeshData& meshData);
	MeshData GetMeshDataFromMQO(LPCTSTR filename); // メタセコイアファイルからメッシュデータを読込む
public:
	MqoMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDev);
};

/// メッシュのビュー変換、射影変換を行なうカメラ
class MeshCamera sealed {
private:
	LPDIRECT3DDEVICE9 pDevice;
public:
	MeshCamera(LPDIRECT3DDEVICE9 pDev);
	void Look(D3DXVECTOR3 eyePoint, D3DXVECTOR3 lookAtPoint);
};

/// メッシュ用ライト
class MeshLight sealed {
private:
	LPDIRECT3DDEVICE9 pDevice;
	D3DLIGHT9 light;
public:
	MeshLight(LPDIRECT3DDEVICE9 pDev);
	void Illume(D3DXVECTOR3 direction);
};

/// フレーム
struct MYFRAME: public D3DXFRAME {
	D3DXMATRIX CombinedTransformationMatrix;
};

/// メッシュコンテナ
struct MYMESHCONTAINER: public D3DXMESHCONTAINER {
	LPDIRECT3DTEXTURE9*  ppTextures;
	DWORD dwWeight;							// 重みの個数（重みとは頂点への影響。）
	DWORD dwBoneAmt;						// ボーンの数
	LPD3DXBUFFER pBoneBuffer;				// ボーン・テーブル
	D3DXMATRIX** ppBoneMatrix;				// 全てのボーンのワールド行列の先頭ポインター
	D3DXMATRIX* pBoneOffsetMatrices;		// フレームとしてのボーンのワールド行列のポインター
};

/// フレームオペレータ
class FrameOperator {
private:
	/// メッシュコンテナのレンダリング
	virtual void RenderMeshContainer(LPDIRECT3DDEVICE9 pDevice, MYMESHCONTAINER* pMeshContainer, MYFRAME* pFrame);
public:
	/// フレームのワールド変換
	virtual void UpdateMatrices(LPD3DXFRAME pFrame, LPD3DXMATRIX world);

	/// フレームのレンダリング
	virtual void Draw(LPDIRECT3DDEVICE9 pDevice, LPD3DXFRAME pFrame);
};

/// Xファイルから読込んだアニメーションメッシュ
class XFileAnimationMesh sealed {
private:
	class MyHierarchy : public ID3DXAllocateHierarchy {
	private:
		void CreateTexture(MYMESHCONTAINER* pMeshContainer, LPDIRECT3DDEVICE9 pDevice, DWORD NumMaterials);
		void SetDefaultMaterial(MYMESHCONTAINER* pMeshContainer);
	public:
		MyHierarchy(){};
		~MyHierarchy(){};
		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);
	};
	LPDIRECT3DDEVICE9			pDevice;										// Direct3Dデバイスオブジェクト
	MyHierarchy					cHierarchy;										// アロケートヒエラルキーオブジェクト
	LPD3DXFRAME					pFrameRoot;										// フレーム
	LPD3DXANIMATIONCONTROLLER	pAnimController;								// アニメーションコントローラ
	LPD3DXANIMATIONSET			pAnimSet[100];									// アニメーションセット
	HRESULT AllocateBoneMatrix(LPD3DXMESHCONTAINER);							// メッシュに行列を割り振る
	HRESULT AllocateAllBoneMatrices(LPD3DXFRAME);								// 再帰コード：フレーム階層を展開して各メッシュに行列を割り振る
	void FreeAnim(LPD3DXFRAME pF);												// 再帰コード：全てのメッシュコンテナをリリース
	FrameOperator				frameOperator;									// フレームオペレータ
public:
	XFileAnimationMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDevice);
	~XFileAnimationMesh(void);

	// 描画
	void Draw(D3DXVECTOR3 position, D3DXMATRIX rotation);

	/// 時間を進行
	void AdvanceTime(double time);

	/// 動きを切り替える
	void ChangeMotion(int motion_number);

	/// フレームを取得
	LPD3DXFRAME GetFrame();
};


/// 自作メッシュ
class HandMadeMesh sealed : public Mesh {
public:
	HandMadeMesh(LPDIRECT3DDEVICE9 pDev);
};
