#pragma once
#include "Mesh.h"

// ブレンド重み付メッシュの頂点データ
struct BlendVertex {
	D3DXVECTOR3 position;
	float weight[3];
	unsigned char matrixIndex[4];
	D3DXVECTOR3 normal;
	D3DXVECTOR2 texture;
};


// ボーン初期化データ
struct BoneInitData {
	bool visible;
	int firstChildIdx;	// 第1子ボーンインデックス。ない場合は-1
	int siblingIdx;		// 次の兄弟ボーンインデックス。ない場合は-1
	D3DXMATRIX initMat;	// 初期姿勢行列
	float length;		// 表示用ボーンの長さ
	BoneInitData() : firstChildIdx(-1), siblingIdx(-1), visible(true), length(0.1f) {}
};


// スキンアニメーションメッシュ初期化データ。独自形式データは一度この構造体に格納する。
struct SkinMeshInitData : public MeshData {
	vector<BlendVertex> blendVertices;
	vector<BoneInitData> boneInitData;
};


// ボーン
struct Bone {
	bool visible;			// 表示するか否か
	float length;			// 表示用ボーンの長さ
	int id;					// ボーンID（通し番号）
	Bone* firstChild;		// 第1子ボーン
	Bone* sibling;			// 次の兄弟ボーン
	D3DXMATRIX initMat;		// 初期姿勢行列
	D3DXMATRIX offsetMat;	// ボーンオフセット行列
	D3DXMATRIX boneMat;		// ボーン姿勢行列
	D3DXMATRIX *combMatAry;	// 合成姿勢行列配列へのポインタ
	Bone() : id(), firstChild(), sibling(), combMatAry() {
		D3DXMatrixIdentity(&initMat);
		D3DXMatrixIdentity(&offsetMat);
		D3DXMatrixIdentity(&boneMat);
	}
};


/// スキンアニメーションメッシュ
class SkinMesh : public Drawable3D {
protected:
	/// 基本 ///
	int time;							// 時間
	SkinMesh(LPDIRECT3DDEVICE9 pDevice);// オブジェクト生成禁止
	LPDIRECT3DDEVICE9 pDevice;			// Direct3Dデバイスオブジェクト
	/// メッシュ ///
	LPD3DXMESH pMesh;					// メッシュ
	vector<D3DMATERIAL9> materials;		// マテリアル配列
	vector<LPDIRECT3DTEXTURE9> textures;// テクスチャ配列
	vector<D3DXBONECOMBINATION> boneCombination;// ボーンコンビネーション配列
	virtual void CreateMesh(const SkinMeshInitData&);												// メッシュを作成
	virtual LPD3DXSKININFO CreateSkinInfo(const SkinMeshInitData& , const D3DVERTEXELEMENT9*);		// メッシュ分割のためのスキン情報を作成
	virtual void DivideMesh(unsigned int numFace, LPD3DXMESH pOrgMesh, LPD3DXSKININFO pSkinInfo);	// ブレンド行列パレットのサイズを越えないようにメッシュを分割
	/// ボーン ///
	vector<Bone> bones;					// ボーン構造体
	vector<D3DXMATRIX> combMat;			// 合成変換行列
	vector<ID3DXMesh*> boneObj;			// 表示用ボーン
	virtual void CreateBoneMatrix(const SkinMeshInitData&);							// ボーン行列, ボーンオフセット行列の作成
	virtual void ConvertInitMatrixInParent(Bone* me, D3DXMATRIX *parentoffsetMat);	// 初期姿勢をローカル座標系から親座標系に変換する再帰関数
	virtual void CreateBoneObj(const SkinMeshInitData&);							// 表示用ボーンを作成
	virtual void ConvertBoneMatrixInWorld(Bone* me, D3DXMATRIX *parentWorldMat);	// ボーン行列をワールド座標系に変換する再帰関数
	/// シェーダ ///
	D3DXHANDLE hTech, hWorld, hView, hProj, hAmbient, hDiffuse, hLightDir, hTexture;// ハンドル
	LPD3DXEFFECT pFX;		// シェーダ
	void CreateShader();	// シェーダの作成

	/// モーション (暫定ハードコーディング)
	virtual void UpdateBoneMatrix();

public:
	virtual ~SkinMesh();
	virtual void AdvanceTime();	// 時間をすすめる
	virtual void Draw(D3DXVECTOR3* position, D3DXMATRIX* rotation, D3DLIGHT9* light, Camera* camera);		// 描画
	virtual void DrawBoneObj(D3DXVECTOR3* position, D3DXMATRIX* rotation, D3DLIGHT9* light, Camera* camera);// ボーンを表示
};


/// 自作スキンメッシュデータ
class HandMadeSkinMesh sealed : public SkinMesh {
	void CreateInitMat(D3DXMATRIX* initMat);
	void CreateSkinMeshData(SkinMeshInitData& skinMeshData);
public:
	HandMadeSkinMesh(LPDIRECT3DDEVICE9 pDevice);
};


/// PMDファイルから読込んだスキンメッシュ
class PmdSkinMesh sealed : public SkinMesh {
	const float scaleToDirectX;
	void CreateInitMat(const PmdStruct::PmdBone* pmdBone, unsigned int i, D3DXMATRIX* initMat, float* length);
	void CopyMaterial(D3DMATERIAL9& material, const PmdStruct::PmdMaterial& pmdMaterial);	// PmdMaterialからD3DMATERIAL9にデータをコピー
public:
	PmdSkinMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDevice);
};