#include "StdAfx.h"
#include "Mesh.h"

///// Meshクラス /////
Mesh::Mesh(LPDIRECT3DDEVICE9 pDev) : pDevice(pDev), pMesh(0), pMeshTextures(0), pMeshMaterials(0) {
}

Mesh::~Mesh() {
	SAFE_RELEASE(pMesh);
	if (pMeshTextures) for (DWORD i = 0; i < dwNumMaterials; ++i) SAFE_RELEASE(pMeshTextures[i]);
	SAFE_DELETE_ARRAY(pMeshTextures);
	SAFE_DELETE_ARRAY(pMeshMaterials);
}

void Mesh::SetMesh(MeshData meshData) {
	D3DXCreateMeshFVF(meshData.faces.size(), meshData.vertices.size(), D3DXMESH_MANAGED, D3DFVF_XYZ, pDevice, &pMesh);
//	D3DXCreateMeshFVF(meshData.faces.size(), meshData.vertices.size(), D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, pDevice, &pMesh);
	Vertex* vertexBuffer;
	pMesh->LockVertexBuffer(0, (void**)&vertexBuffer);
	for (unsigned int i = 0; i < meshData.vertices.size(); ++i) {
		vertexBuffer[i].position = meshData.vertices[i].position;
//		vertexBuffer[i].normal = meshData.vertices[i].normal;
	}
	pMesh->UnlockVertexBuffer();
	unsigned short* indexBuffer;
	pMesh->LockIndexBuffer(0, (void**)&indexBuffer);
	for (unsigned int i = 0; i < meshData.faces.size(); ++i) for (unsigned int j = 0; j < 3; ++j) indexBuffer[3*i + j] = meshData.faces[i].indices[j];
	pMesh->UnlockIndexBuffer();
	unsigned long* attributeBuffer;
	pMesh->LockAttributeBuffer(0, &attributeBuffer);
	for (unsigned int i = 0; i < meshData.faces.size(); ++i) attributeBuffer[i] = meshData.faces[i].material_number;
	pMesh->UnlockAttributeBuffer();
	dwNumMaterials = meshData.material.size();
    pMeshMaterials = new D3DMATERIAL9[dwNumMaterials];
    pMeshTextures  = new LPDIRECT3DTEXTURE9[dwNumMaterials];
	for (DWORD i = 0; i < dwNumMaterials; ++i) { 
		pMeshMaterials[i] = meshData.material[i];
		char tex_filename[256] = {0};		// UNICODE未対応テクスチャファイル名
		TCHAR textureFilename[256] = {0};	// UNICODE/マルチバイト両対応テクスチャファイル名
        pMeshTextures[i] = 0;
		if (strcpy_s(tex_filename, meshData.texture_filename[i].c_str())) throw TEXT("テクスチャの読み込みに失敗しました");
#ifdef UNICODE
		if (strlen(tex_filename) > 0) MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, tex_filename, strlen(tex_filename), textureFilename, (sizeof textureFilename)/2);
#else
		if (strlen(tex_filename) > 0) strcpy_s(textureFilename, tex_filename);
#endif
		if (lstrlen(textureFilename) > 0) // UNICODE/マルチバイト両対応テクスチャファイル名からテクスチャを作成
			if(FAILED(D3DXCreateTextureFromFileEx(pDevice, textureFilename, 0, 0, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0xff000000, 0, 0, &pMeshTextures[i]))) throw TEXT("テクスチャの読み込みに失敗しました");
	}
}

void Mesh::Draw(D3DXVECTOR3 position, D3DXMATRIX rotation) {
	D3DXMATRIX matWorld, matTrans;
	D3DXMatrixTranslation(&matTrans, position.x, position.y, position.z);
	matWorld = rotation*matTrans;
    pDevice->SetTransform(D3DTS_WORLD, &matWorld);
	pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);	// ポリゴンのDiffuse色の透明度をテクスチャに反映させる
	pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
	for (DWORD i = 0; i < dwNumMaterials; ++i) {
		pDevice->SetMaterial(&pMeshMaterials[i]);
		pDevice->SetTexture(0, pMeshTextures[i]); 
		pMesh->DrawSubset(i);
    }
}

LPD3DXMESH Mesh::GetMesh() { return pMesh; }

int Mesh::GetNumMaterial() { return dwNumMaterials; }



///// MqoMeshクラス /////
MqoMesh::MqoMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDev) : Mesh(pDev) {
	MeshData meshData = GetMeshDataFromMQO(filename);
	SetMesh(meshData);
}

/// マテリアル行の文字列から指定した名前の値の配列を取得
/// @return 指定した名前の値の配列。指定した名前がなければ空配列。
/// @param str マテリアル行の文字列
/// @param name 値の名前	shader(%d) vcol(%d) col(%.3f %.3f %.3f %.3f) dif(%.3f) amb(%.3f) emi(%.3f) spc(%.3f) power(%.2f) tex(%s) 
///							alpha(%s) bump(%s) proj_type(%d) proj_pos(%.3f %.3f %.3f) proj_scale(%.3f %.3f %.3f) proj_angle(%.3f %.3f %.3f)
/// @param n 値の個数
template <typename T> vector<T> MqoMesh::MaterialPickOut(string str, char* name, int n) {
	vector<T> ts;
	T t;
	int offset = str.find("(", str.find(name, str.find("\"", str.find("\""))));// "材質名"の後の、nameの後の、括弧の位置
	if (offset == string::npos) return ts;
	else ++offset;								// 括弧の次の位置
	int count = str.find(")", offset) - offset;	// offsetから閉じ括弧までの長さ
	string s = str.substr(offset, count);		// 指定した名前のデータを抽出
	stringstream ss(s);
	for (int i = 0; i < n; ++i) {
		ss >> t;
		ts.push_back(t);
	}
	return ts;
}

void MqoMesh::LoadMaterial(ifstream& ifs, MeshData& meshData){
	string str;
	while (!ifs.eof()) {
		getline(ifs, str);
		if (str.find("}") == 0) break;	// 行の先頭に"}"があると終了
		vector<float> col = MaterialPickOut<float>(str, "col", 4);
		vector<float> dif = MaterialPickOut<float>(str, "dif", 1);
		vector<float> amb = MaterialPickOut<float>(str, "amb", 1);
		vector<float> emi = MaterialPickOut<float>(str, "emi", 1);
		vector<float> spc = MaterialPickOut<float>(str, "spc", 1);
		vector<float> power = MaterialPickOut<float>(str, "power", 1);
		vector<string> tex = MaterialPickOut<string>(str, "tex", 1);
		D3DMATERIAL9 material;
		material.Diffuse.r = dif[0]*col[0];
		material.Diffuse.g = dif[0]*col[1];
		material.Diffuse.b = dif[0]*col[2];
		material.Diffuse.a = dif[0]*col[3];
		material.Ambient.r = amb[0]*col[0];
		material.Ambient.g = amb[0]*col[1];
		material.Ambient.b = amb[0]*col[2];
		material.Ambient.a = amb[0]*col[3];
		material.Emissive.r = emi[0]*col[0];
		material.Emissive.g = emi[0]*col[1];
		material.Emissive.b = emi[0]*col[2];
		material.Emissive.a = emi[0]*col[3];
		material.Specular.r = spc[0]*col[0];
		material.Specular.g = spc[0]*col[1];
		material.Specular.b = spc[0]*col[2];
		material.Specular.a = spc[0]*col[3];
		material.Power = power[0];
		meshData.material.push_back(material);
		//if (!tex.empty()) meshData.texture_filename.push_back(tex[0]);
		//else meshData.texture_filename.push_back("");
		meshData.texture_filename.push_back("");
	}
}

void MqoMesh::LoadObject(ifstream& ifs, MeshData& meshData){
	string str;
	int offset = meshData.vertices.size();
	while (!ifs.eof()) {
		getline(ifs, str);
		if (str.find("}") == 0) break;	// 行の先頭に"}"があると終了
		if (str.find("vertex ") != string::npos) {	// 頂点データ読み込み開始
			while (!ifs.eof()) {
				getline(ifs, str);
				if (str.find("}") != string::npos) break;	// "}"があると終了
				stringstream ss(str);
				Vertex v;
				ss >> v.position.x >> v.position.y >> v.position.z;
				v.position = 0.01f*v.position;			// 倍率
				meshData.vertices.push_back(v);
			}
		}
		if (str.find("face ") != string::npos) {		// ポリゴンデータ読み込み開始
			while (!ifs.eof()) {
				getline(ifs, str);
				if (str.find("}") != string::npos) break;	// "}"があると終了
				if (str[str.find_first_not_of(" \t")] != '3') throw TEXT("MQOファイルフォーマットエラー1"); // 空白以外の先頭文字が3以外だとエラー
				str = str.substr(str.find("(") + 1);		// 最初の"("までをカット
				stringstream ss(str);
				Face f;
				ss >> f.indices[2] >> f.indices[1] >> f.indices[0];	// 頂点データをメアセコイアフォーマットとは逆順に格納
				for (int i = 0; i < 3; ++i) f.indices[i] += offset;
				str = str.substr(str.find("(") + 1);	// 次の"("までをカット
				ss.str(str);
				ss >> f.material_number;
				meshData.faces.push_back(f);
			}
		}
	}
}

MeshData MqoMesh::GetMeshDataFromMQO(LPCTSTR filename) {
	MeshData meshData;
	ifstream ifs(filename);
	string str;
	while(!ifs.eof()) {
		getline(ifs, str);
		if (str.find("Material") == 0) {
			if (*--str.end() != '{') throw TEXT("MQOファイルフォーマットエラー2");
			LoadMaterial(ifs, meshData);
		}
		if (str.find("Object") == 0) {
			if (*--str.end() != '{') throw TEXT("MQOファイルフォーマットエラー3");
			LoadObject(ifs, meshData);
		}
	}
	return meshData;
}


///// Xファイル メッシュ /////
XFileMesh::XFileMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDev) : Mesh(pDev) {
	LPD3DXBUFFER pD3DXMtrlBuffer = NULL;
	if (FAILED( D3DXLoadMeshFromX(filename, D3DXMESH_SYSTEMMEM, pDevice, NULL, &pD3DXMtrlBuffer, NULL, &dwNumMaterials, &pMesh))) throw TEXT("Xファイルの読み込みに失敗しました");
	D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
    pMeshMaterials = new D3DMATERIAL9[dwNumMaterials];
    pMeshTextures  = new LPDIRECT3DTEXTURE9[dwNumMaterials];
	for (DWORD i = 0; i < dwNumMaterials; ++i) { 
		pMeshMaterials[i] = d3dxMaterials[i].MatD3D;
        pMeshTextures[i] = 0;
		TCHAR textureFilename[256] = {0};
#ifdef UNICODE
		if (d3dxMaterials[i].pTextureFilename) MultiByteToWideChar(CP_OEMCP,MB_PRECOMPOSED, d3dxMaterials[i].pTextureFilename, strlen(d3dxMaterials[i].pTextureFilename), textureFilename, (sizeof textureFilename)/2);
#else
		if (d3dxMaterials[i].pTextureFilename) strcpy_s(textureFilename, d3dxMaterials[i].pTextureFilename);
#endif
		if (textureFilename != NULL && lstrlen(textureFilename) > 0)
			if(FAILED(D3DXCreateTextureFromFileEx(pDevice, textureFilename, 0, 0, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0xff000000, 0, 0, &pMeshTextures[i]))) throw TEXT("テクスチャの読み込みに失敗しました");
	}
	pD3DXMtrlBuffer->Release();
}



///// メッシュ用カメラ /////
MeshCamera::MeshCamera(LPDIRECT3DDEVICE9 pDev) : pDevice(pDev) {
}

void MeshCamera::Look(D3DXVECTOR3 eyePoint, D3DXVECTOR3 lookAtPoint) {
	const D3DXVECTOR3 upVec(0, 1, 0);									// 上方ベクトル
	const float viewAngle = D3DX_PI/4;									// 視野角
	const float aspect = (float)WINDOW_WIDTH/(float)WINDOW_HEIGHT;		// アスペクト比 = ビュー空間の幅/高さ (MSDNでは高さ/幅になっているが間違い)
	const float nearZ = 0.1f;											// 最近点 (0にするとポリゴンが近い時にチラツキが起こる)
	const float farZ = 2000.0f;											// 最遠点
	D3DXMATRIXA16 matView, matProj;
	D3DXMatrixLookAtLH(&matView, &eyePoint, &lookAtPoint, &upVec);
	D3DXMatrixPerspectiveFovLH(&matProj, viewAngle, aspect, nearZ, farZ);
	pDevice->SetTransform(D3DTS_VIEW, &matView);
	pDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}

///// メッシュ用ライト /////
MeshLight::MeshLight(LPDIRECT3DDEVICE9 pDev) : pDevice(pDev) {
	const D3DCOLORVALUE diffuse = {1, 1, 1, 0};
	const D3DCOLORVALUE specular = {1, 1, 1, 0};
	const D3DCOLORVALUE ambient = {1, 1, 1, 0};
	const float range = 200.0f;
	const D3DXVECTOR3 direction(-0.1f, -1.0f, -0.1f);
	ZeroMemory(&light, sizeof(D3DLIGHT9));
	light.Type      = D3DLIGHT_DIRECTIONAL;
	light.Ambient	= ambient;
	light.Diffuse	= diffuse;
	light.Specular	= specular;
	light.Range     = range;
	light.Direction = direction;
}

void MeshLight::Illume(D3DXVECTOR3 direction) {
	light.Direction = direction;
	pDevice->SetLight(0, &light );
	pDevice->LightEnable(0, TRUE );
}

///// フレームオペレーター /////
void FrameOperator::RenderMeshContainer(LPDIRECT3DDEVICE9 pDevice, MYMESHCONTAINER* pMeshContainer, MYFRAME* pFrame) {
	DWORD i, k, dwBlendMatrixAmt, dwPrevBoneID;
    LPD3DXBONECOMBINATION pBoneCombination;
    UINT iMatrixIndex;
    D3DXMATRIX matStack;
	if (pMeshContainer->pSkinInfo) {		// スキンメッシュの場合
		pBoneCombination = (LPD3DXBONECOMBINATION)pMeshContainer->pBoneBuffer->GetBufferPointer();
		dwPrevBoneID = UINT_MAX;
		for (i = 0; i < pMeshContainer->dwBoneAmt; ++i) {
			dwBlendMatrixAmt = 0;
			for (k = 0; k < pMeshContainer->dwWeight; ++k) if (pBoneCombination[i].BoneId[k] != UINT_MAX) dwBlendMatrixAmt = k;
			pDevice->SetRenderState(D3DRS_VERTEXBLEND, dwBlendMatrixAmt);
			for (k = 0; k < pMeshContainer->dwWeight; ++k) {
				iMatrixIndex = pBoneCombination[i].BoneId[k];
				if (iMatrixIndex != UINT_MAX) {
					matStack = pMeshContainer->pBoneOffsetMatrices[iMatrixIndex]*(*pMeshContainer->ppBoneMatrix[iMatrixIndex]);
					pDevice->SetTransform(D3DTS_WORLDMATRIX(k), &matStack);
				}
			}
			pDevice->SetMaterial(&pMeshContainer->pMaterials[pBoneCombination[i].AttribId].MatD3D);
			pDevice->SetTexture(0, pMeshContainer->ppTextures[pBoneCombination[i].AttribId]);
			dwPrevBoneID = pBoneCombination[i].AttribId;
			pMeshContainer->MeshData.pMesh->DrawSubset(i);
			pDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
		}
	}
	else {									// 通常メッシュの場合
		pDevice->SetTransform(D3DTS_WORLD, &pFrame->CombinedTransformationMatrix);
		for (i = 0; i < pMeshContainer->NumMaterials; ++i) {
			pDevice->SetMaterial(&pMeshContainer->pMaterials[i].MatD3D);
			pDevice->SetTexture(0, pMeshContainer->ppTextures[i]);
			pMeshContainer->MeshData.pMesh->DrawSubset(i);
		}
	}
}

void FrameOperator::UpdateMatrices(LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix) {
    MYFRAME *pFrame = (MYFRAME*)pFrameBase;	
    if (pParentMatrix) D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix);
    else pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix;
    if (pFrame->pFrameSibling) UpdateMatrices(pFrame->pFrameSibling, pParentMatrix);
    if (pFrame->pFrameFirstChild) UpdateMatrices(pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix);
}

void FrameOperator::Draw(LPDIRECT3DDEVICE9 pDevice,LPD3DXFRAME pFrameBase) {
	MYFRAME* pFrame = (MYFRAME*)pFrameBase;
    MYMESHCONTAINER* pMeshContainer = (MYMESHCONTAINER*)pFrame->pMeshContainer;	
    while (pMeshContainer) {
        RenderMeshContainer(pDevice, pMeshContainer, pFrame);
        pMeshContainer = (MYMESHCONTAINER*)pMeshContainer->pNextMeshContainer;
    }
    if (pFrame->pFrameSibling) Draw(pDevice,pFrame->pFrameSibling);
    if (pFrame->pFrameFirstChild) Draw(pDevice,pFrame->pFrameFirstChild);
}


///// アニメーションメッシュ /////
HRESULT XFileAnimationMesh::MyHierarchy::CreateFrame(LPCSTR Name, LPD3DXFRAME *ppNewFrame) {
    HRESULT hr = S_OK;
    MYFRAME *pFrame;
    *ppNewFrame = NULL;
    pFrame = new MYFRAME;
    if (!pFrame) return E_OUTOFMEMORY;
	pFrame->Name = (LPSTR)(new TCHAR[strlen(Name) + 1]);	
    if (!pFrame->Name) return E_FAIL;
	strcpy_s(pFrame->Name, strlen(Name) + 1, Name);
    D3DXMatrixIdentity(&pFrame->TransformationMatrix);
    D3DXMatrixIdentity(&pFrame->CombinedTransformationMatrix);
    pFrame->pMeshContainer = NULL;
    pFrame->pFrameSibling = NULL;
    pFrame->pFrameFirstChild = NULL;
    *ppNewFrame = pFrame;
    return S_OK;
}

void XFileAnimationMesh::MyHierarchy::CreateTexture(MYMESHCONTAINER* pMeshContainer, LPDIRECT3DDEVICE9 pDevice, DWORD NumMaterials) {
	for (DWORD iMaterial = 0; iMaterial < NumMaterials; ++iMaterial) {
		if (pMeshContainer->pMaterials[iMaterial].pTextureFilename) {
			// テクスチャファイル名のUnicode対応
			TCHAR strTexturePath[MAX_PATH] = {0};
#ifdef UNICODE
			MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, pMeshContainer->pMaterials[iMaterial].pTextureFilename, strlen(pMeshContainer->pMaterials[iMaterial].pTextureFilename), strTexturePath, (sizeof strTexturePath)/2);
#else
			strcpy_s(strTexturePath, pMeshContainer->pMaterials[iMaterial].pTextureFilename);
#endif
			if(FAILED( D3DXCreateTextureFromFile(pDevice, strTexturePath, &pMeshContainer->ppTextures[iMaterial]))) pMeshContainer->ppTextures[iMaterial] = NULL;
			pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
		}
	}
}

void XFileAnimationMesh::MyHierarchy::SetDefaultMaterial(MYMESHCONTAINER* pMeshContainer) {
	pMeshContainer->pMaterials[0].pTextureFilename = NULL;
	memset(&pMeshContainer->pMaterials[0].MatD3D, 0, sizeof(D3DMATERIAL9));
	pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
	pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
	pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
	pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
}

HRESULT XFileAnimationMesh::MyHierarchy::CreateMeshContainer(LPCSTR Name, CONST D3DXMESHDATA* pMeshData, CONST D3DXMATERIAL* pMaterials, CONST D3DXEFFECTINSTANCE* pEffectInstances, DWORD NumMaterials, CONST DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppMeshContainer) {
    MYMESHCONTAINER *pMeshContainer = NULL;
    INT iFacesAmount;
    LPDIRECT3DDEVICE9 pDevice = NULL;
    LPD3DXMESH pMesh = NULL;
    *ppMeshContainer = NULL;
	DWORD dwBoneAmt=0;
    pMesh = pMeshData->pMesh;
    pMeshContainer = new MYMESHCONTAINER;
    if (!pMeshContainer) return E_OUTOFMEMORY;
    ZeroMemory(pMeshContainer, sizeof(MYMESHCONTAINER));
	pMeshContainer->Name = (LPSTR)(new TCHAR[strlen(Name) + 1]);	
    if (!pMeshContainer->Name) return E_FAIL;
	strcpy_s((char*)pMeshContainer->Name, strlen(Name) + 1, Name);
    pMesh->GetDevice(&pDevice);
    iFacesAmount = pMesh->GetNumFaces();  
	pMeshContainer->MeshData.pMesh = pMesh;
	pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
    pMeshContainer->NumMaterials = max(1, NumMaterials);
    pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
    pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
    pMeshContainer->pAdjacency = new DWORD[iFacesAmount*3];
    if ((pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL)) return E_FAIL;
    memcpy(pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * iFacesAmount*3);
    memset(pMeshContainer->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9)*pMeshContainer->NumMaterials);
    if (NumMaterials > 0) {
        memcpy(pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL)*NumMaterials);
		CreateTexture(pMeshContainer, pDevice, NumMaterials);
    }
    else SetDefaultMaterial(pMeshContainer);
	if (pSkinInfo) {			//当該メッシュがスキン情報を持っている場合（スキンメッシュ固有の処理）
		pMeshContainer->pSkinInfo = pSkinInfo;
        pSkinInfo->AddRef();
		dwBoneAmt = pSkinInfo->GetNumBones();
		pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[dwBoneAmt];
		for (DWORD i = 0; i < dwBoneAmt; ++i) memcpy(&pMeshContainer->pBoneOffsetMatrices[i], pMeshContainer->pSkinInfo->GetBoneOffsetMatrix(i), sizeof(D3DMATRIX));
		if(FAILED(pMeshContainer->pSkinInfo->ConvertToBlendedMesh(pMesh, NULL, pMeshContainer->pAdjacency, NULL, NULL, NULL, &pMeshContainer->dwWeight, &pMeshContainer->dwBoneAmt, &pMeshContainer->pBoneBuffer, &pMeshContainer->MeshData.pMesh))) return E_FAIL;
	}
	*ppMeshContainer = pMeshContainer;
	SAFE_RELEASE(pDevice);
    return S_OK;
}

HRESULT XFileAnimationMesh::MyHierarchy::DestroyFrame(LPD3DXFRAME pFrameToFree) {
    SAFE_DELETE_ARRAY(pFrameToFree->Name);
	SAFE_DELETE(pFrameToFree);
    return S_OK; 
}

HRESULT XFileAnimationMesh::MyHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase) {
    DWORD iMaterial;
    MYMESHCONTAINER *pMeshContainer = (MYMESHCONTAINER*)pMeshContainerBase;
    SAFE_DELETE_ARRAY(pMeshContainer->Name);
	SAFE_RELEASE(pMeshContainer->pSkinInfo);
    SAFE_DELETE_ARRAY(pMeshContainer->pAdjacency);
    SAFE_DELETE_ARRAY(pMeshContainer->pMaterials);
	SAFE_DELETE_ARRAY(pMeshContainer->ppBoneMatrix);
    if (pMeshContainer->ppTextures) for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; ++iMaterial) SAFE_RELEASE(pMeshContainer->ppTextures[iMaterial]);
    SAFE_RELEASE(pMeshContainer->MeshData.pMesh);
	SAFE_RELEASE(pMeshContainer->pBoneBuffer);
	SAFE_DELETE_ARRAY(pMeshContainer->pBoneOffsetMatrices);
	SAFE_DELETE_ARRAY(pMeshContainer->ppBoneMatrix);
    SAFE_DELETE(pMeshContainer);
    return S_OK;
}

XFileAnimationMesh::XFileAnimationMesh(LPCTSTR filename, LPDIRECT3DDEVICE9 pDev) : pDevice(pDev) {
	if (FAILED(D3DXLoadMeshHierarchyFromX(filename, D3DXMESH_MANAGED, pDevice, &cHierarchy, NULL, &pFrameRoot, &pAnimController))) throw TEXT("Xファイルの読み込みに失敗しました");
	for(DWORD i = 0; i < pAnimController->GetNumAnimationSets(); ++i) {	//アニメーショントラックを得る
		D3DXTRACK_DESC TrackDesc;
		ZeroMemory(&TrackDesc,sizeof(TrackDesc));
		TrackDesc.Weight = 1;
		TrackDesc.Speed = 1;
		TrackDesc.Enable = 1;
		pAnimController->SetTrackDesc(i, &TrackDesc);
		pAnimController->GetAnimationSet(i, &pAnimSet[i]);
		pAnimController->SetTrackAnimationSet(i, pAnimSet[i]);
	}
	AllocateAllBoneMatrices(pFrameRoot);
	ChangeMotion(0);
}

XFileAnimationMesh::~XFileAnimationMesh() {
	FreeAnim(pFrameRoot);
	cHierarchy.DestroyFrame(pFrameRoot);
	SAFE_RELEASE( pAnimController);
}

void XFileAnimationMesh::FreeAnim(LPD3DXFRAME pF) {
	if (pF->pMeshContainer) cHierarchy.DestroyMeshContainer(pF->pMeshContainer);
    if (pF->pFrameSibling) FreeAnim(pF->pFrameSibling);
    if (pF->pFrameFirstChild) FreeAnim(pF->pFrameFirstChild);
}

HRESULT XFileAnimationMesh::AllocateBoneMatrix(LPD3DXMESHCONTAINER pMeshContainerBase) {
    MYFRAME *pFrame = NULL;
	DWORD dwBoneAmt = 0;
    MYMESHCONTAINER *pMeshContainer = (MYMESHCONTAINER*)pMeshContainerBase;
    if (!pMeshContainer->pSkinInfo) return S_OK;
	dwBoneAmt = pMeshContainer->pSkinInfo->GetNumBones();
    pMeshContainer->ppBoneMatrix = new D3DXMATRIX*[dwBoneAmt];
    for (DWORD i = 0; i < dwBoneAmt; ++i) {
		pFrame = (MYFRAME*)D3DXFrameFind(pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(i));
        if (!pFrame) return E_FAIL;
		pMeshContainer->ppBoneMatrix[i] = &pFrame->CombinedTransformationMatrix;
	}    
    return S_OK;
}

HRESULT XFileAnimationMesh::AllocateAllBoneMatrices(LPD3DXFRAME pFrame) {
    if (pFrame->pMeshContainer) if (FAILED(AllocateBoneMatrix(pFrame->pMeshContainer))) return E_FAIL;
    if (pFrame->pFrameSibling) if (FAILED(AllocateAllBoneMatrices(pFrame->pFrameSibling))) return E_FAIL;
    if (pFrame->pFrameFirstChild) if (FAILED(AllocateAllBoneMatrices(pFrame->pFrameFirstChild))) return E_FAIL;
    return S_OK;
}

void XFileAnimationMesh::Draw(D3DXVECTOR3 position, D3DXMATRIX attitude) {
	D3DXMATRIX matTrans;
	D3DXMatrixTranslation(&matTrans, position.x, position.y, position.z);
	D3DXMATRIX matWorld = attitude*matTrans;
    pDevice->SetTransform(D3DTS_WORLD, &matWorld);
	frameOperator.UpdateMatrices(pFrameRoot, &matWorld);
	frameOperator.Draw(pDevice, pFrameRoot);
}

void XFileAnimationMesh::AdvanceTime(double time) {
	pAnimController->AdvanceTime(time, 0);
}

void XFileAnimationMesh::ChangeMotion(int motion_number) {
	int N = pAnimController->GetNumAnimationSets();
	for (int i = 0; i < N; ++i) pAnimController->SetTrackEnable(i, false);
	pAnimController->SetTrackEnable(motion_number, true);
}

LPD3DXFRAME XFileAnimationMesh::GetFrame() { return pFrameRoot; }


HandMadeMesh::HandMadeMesh(LPDIRECT3DDEVICE9 pDev) : Mesh(pDev) {
	// 空メッシュの作成。頂点データはXYZ座標のみ
	int numPolygon = 2;	// ポリゴン数
	int numVertex = 4;	// 頂点数
	D3DXCreateMeshFVF(numPolygon, numVertex, D3DXMESH_MANAGED, D3DFVF_XYZ, pDevice, &pMesh);

	// メッシュの頂点データをセット
	Vertex* vertexBuffer;
	pMesh->LockVertexBuffer(0, (void**)&vertexBuffer);
	vertexBuffer[0].position = D3DXVECTOR3(-0.5f, 0, -0.5f);
	vertexBuffer[1].position = D3DXVECTOR3(+0.5f, 0, -0.5f);
	vertexBuffer[2].position = D3DXVECTOR3(-0.5f, 0, +0.5f);
	vertexBuffer[3].position = D3DXVECTOR3(+0.5f, 0, +0.5f);
	pMesh->UnlockVertexBuffer();

	// メッシュのポリゴンデータ(頂点番号の集まり)をセット
	unsigned short* indexBuffer;
	pMesh->LockIndexBuffer(0, (void**)&indexBuffer);
	indexBuffer[0] = 2;
	indexBuffer[1] = 3;
	indexBuffer[2] = 1;
	indexBuffer[3] = 0;
	indexBuffer[4] = 2;
	indexBuffer[5] = 1;
	pMesh->UnlockIndexBuffer();

	// メッシュの各ポリゴンの色番号をセット
	unsigned long* attributeBuffer;
	pMesh->LockAttributeBuffer(0, &attributeBuffer);
	attributeBuffer[0] = 0;
	attributeBuffer[1] = 0;
	pMesh->UnlockAttributeBuffer();

	// 各色番号に色をセット
	D3DMATERIAL9 material = {{0, 1, 0, 1}, {0, 0.5f, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, 0};
	dwNumMaterials = 1;
    pMeshMaterials = new D3DMATERIAL9[dwNumMaterials];
	pMeshMaterials[0] = material;

	// テクスチャをセット(今回はなし)
	pMeshTextures  = new LPDIRECT3DTEXTURE9[dwNumMaterials];
	pMeshTextures[0] = 0;
}