//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * Xメッシュトランスレータ実装
 * @author Junpee
 */

#include <LampBasic.h>
#include "XTranslator/XMeshTranslator.h"
#include "XTranslator/XMaterialTranslator.h"
#include <Core/InputOutput/FilePath.h>
#include <Graphics/System/LampGraphics.h>
#include <Graphics/SceneNode/SceneNodeManager.h>
#include <Graphics/Model/ModelManager.h>
#include <Graphics/Mesh/MeshManager.h>
#include <Graphics/MeshData/MeshDataManager.h>

// シーンノードサフィックス
const String XMeshTranslator::sceneNodeSuffix = "SceneNode";
// モデルサフィックス
const String XMeshTranslator::modelSuffix = "Model";
// メッシュサフィックス
const String XMeshTranslator::meshSuffix = "Mesh";
// メッシュデータサフィックス
const String XMeshTranslator::meshDataSuffix = "MeshData";

//------------------------------------------------------------------------------
// コンストラクタ
XMeshTranslator::XMeshTranslator() : xMesh_(NULL), xMaterialsBuffer_(NULL),
	xMaterialCount_(0), xAttributes_(NULL), xAttributeCount_(0),
	vertexSize_(0), positionOffset_(-1), normalOffset_(-1), uvOffset_(-1),
	scene_(NULL), name_(""), sceneNode_(NULL), model_(NULL){
}
//------------------------------------------------------------------------------
// デストラクタ
XMeshTranslator::~XMeshTranslator(){
	SafeArrayDelete(xAttributes_);
	SafeRelease(xMaterialsBuffer_);
	SafeRelease(xMesh_);
}
//------------------------------------------------------------------------------
// Xファイルの変換
bool XMeshTranslator::translateXFile(const FilePath& xFilePath, Scene* scene){
	if(!xFilePath.existFile()){
		ErrorOut("XFileConverter::initialize() Xファイルが見つかりません");
		return false;
	}
	name_ = xFilePath.getName();
	scene_ = scene;
	
	// Xファイルのロード
	// システムメモリにロードする
	u_int options = (D3DXMESH_SYSTEMMEM);
	// 隣接情報とエフェクトは取得しない
	DirectXCheck(D3DXLoadMeshFromX(xFilePath.getPath().getBytes(), options,
		LampGraphics::getDirect3DDevice(), NULL, &xMaterialsBuffer_,
		NULL, (u_long*)&xMaterialCount_, &xMesh_));
	// 法線が無い場合は自動生成する
	if((xMesh_->GetFVF() & D3DFVF_NORMAL) == 0){
		Direct3DXMesh* tempMesh;
		DirectXCheck(xMesh_->CloneMeshFVF(xMesh_->GetOptions(),
			(xMesh_->GetFVF() | D3DFVF_NORMAL),
			LampGraphics::getDirect3DDevice(), &tempMesh));
		D3DXComputeNormals(tempMesh, NULL);
		SafeRelease(xMesh_);
		xMesh_ = tempMesh;
	}
	// アトリビュートを作成する
	DirectXCheck(xMesh_->OptimizeInplace(
		(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT),
		NULL, NULL, NULL, NULL));

	// アトリビュートの解析
	if(!analyzeAttribute()){ return false; }

	// 頂点記述の解析
	if(!analyzeDeclaration()){ return false; }

	// モデルの初期化
	initializeModel();

	// メッシュの作成
	D3DXMATERIAL* xMaterials;
	xMaterials = (D3DXMATERIAL*)xMaterialsBuffer_->GetBufferPointer();
	for(u_int i = 0; i < xAttributeCount_; i++){
		const D3DXATTRIBUTERANGE& attribute = xAttributes_[i];
		const D3DXMATERIAL& material = xMaterials[attribute.AttribId];
		if(!analyzeMesh(xFilePath, attribute, material)){ return false; }
	}
	return true;
}
//------------------------------------------------------------------------------
// モデルの初期化
void XMeshTranslator::initializeModel(){
	Assert(scene_ != NULL);
	Assert(!name_.isEmpty());
	Assert(xMesh_ != NULL);
	// シーンノード初期化
	SceneNodeManager* sceneNodeManager = scene_->getSceneNodeManager();
	String sceneNodeName = sceneNodeManager->rename(name_ + sceneNodeSuffix);
	sceneNode_ = sceneNodeManager->createSceneNode(sceneNodeName);
	scene_->getRootNode()->addSceneNode(sceneNode_);
	// モデル初期化
	ModelManager* modelManager = scene_->getModelManager();
	String modelName = modelManager->rename(name_ + modelSuffix);
	model_ = modelManager->createStandardModel(modelName);
	sceneNode_->addSceneLeaf(model_);
}
//------------------------------------------------------------------------------
// アトリビュートの解析
bool XMeshTranslator::analyzeAttribute(){
	Assert(xMesh_ != NULL);
	DirectXCheck(xMesh_->GetAttributeTable(NULL, (u_long*)&xAttributeCount_));
	xAttributes_ = new D3DXATTRIBUTERANGE[xAttributeCount_];
	DirectXCheck(xMesh_->GetAttributeTable(
		xAttributes_, (u_long*)&xAttributeCount_));
	return true;
}
//------------------------------------------------------------------------------
// 頂点記述の解析
bool XMeshTranslator::analyzeDeclaration(){
	Assert(xMesh_ != NULL);
	// 頂点サイズの解析
	vertexSize_ = xMesh_->GetNumBytesPerVertex();
	// 各頂点記述の解析
	D3DVertexElement elements[MAX_FVF_DECL_SIZE];
	DirectXCheck(xMesh_->GetDeclaration(elements));
	for(int i = 0; i < MAX_FVF_DECL_SIZE; i++){
		const D3DVertexElement& element = elements[i];
		// D3DDECL_END判定
		if(element.Stream == 0xff){ break; }
		// ストリーム0以外は無視
		if(element.Stream != 0){ continue; }
		// プロセッシングメソッドはDefaultのみ
		if(element.Method != D3DDECLMETHOD_DEFAULT){ continue; }
		// 位置の解析
		if(element.Usage == D3DDECLUSAGE_POSITION){
			// float3以外は無視
			if(element.Type != D3DDECLTYPE_FLOAT3){ continue; }
			// 使用方法インデックスは0のみ
			if(element.UsageIndex != 0){ continue; }
			positionOffset_ = element.Offset;
		// 法線の解析
		}else if(element.Usage == D3DDECLUSAGE_NORMAL){
			// float3以外は無視
			if(element.Type != D3DDECLTYPE_FLOAT3){ continue; }
			// 使用方法インデックスは0のみ
			if(element.UsageIndex != 0){ continue; }
			normalOffset_ = element.Offset;
		// UVの解析
		}else if(element.Usage == D3DDECLUSAGE_TEXCOORD){
			// float2以外は無視
			if(element.Type != D3DDECLTYPE_FLOAT2){ continue; }
			// 使用方法インデックスは0のみ
			if(element.UsageIndex != 0){ continue; }
			uvOffset_ = element.Offset;
		}
	}
	if(positionOffset_ == -1){
		ErrorOut("XMeshTranslator::analyzeDeclaration() "
			"頂点記述に位置がありません");
		return false;
	}
	return true;
}
//------------------------------------------------------------------------------
// メッシュの解析
bool XMeshTranslator::analyzeMesh(const FilePath& xFilePath,
	const D3DXATTRIBUTERANGE& attribute, const D3DXMATERIAL& material){
	// メッシュを作成する
	MeshManager* meshManager = scene_->getMeshManager();
	String meshName = meshManager->rename(name_ + meshSuffix);
	Mesh* mesh = meshManager->createRigidMesh(meshName);
	model_->addMesh(mesh);
	// メッシュデータを作成する
	MeshDataManager* meshDataManager = scene_->getMeshDataManager();
	String meshDataName = meshDataManager->rename(name_ + meshDataSuffix);
	MeshData* meshData = meshDataManager->createMeshData(meshDataName);
	mesh->setMeshData(meshData);
	// マテリアルを作成する
	XMaterialTranslator xMaterialTranslator;
	if(!xMaterialTranslator.translate(xFilePath, scene_, name_, material)){
		return false;
	}
	mesh->setMaterial(xMaterialTranslator.getMaterial());

	// メッシュ情報の解析
	meshData->setPrimitiveType(Mesh::indexedTriangleList);
	// インデックスバッファの解析
	u_short* indexAddress;
	DirectXCheck(xMesh_->LockIndexBuffer(
		D3DLOCK_READONLY, (void**)&indexAddress));
	indexAddress += (attribute.FaceStart * 3);
	u_int faceCount = attribute.FaceCount;
	u_int indexOffset = attribute.VertexStart;
	meshData->setVertexIndexCount(faceCount * 3);
	for(u_int i = 0; i < faceCount; i++){
		int offset = i * 3;
		// 面の回転方向を反転させる
		meshData->setVertexIndex(offset + 2, (*indexAddress) - indexOffset);
		indexAddress++;
		meshData->setVertexIndex(offset + 1, (*indexAddress) - indexOffset);
		indexAddress++;
		meshData->setVertexIndex(offset + 0, (*indexAddress) - indexOffset);
		indexAddress++;
	}
	DirectXCheck(xMesh_->UnlockIndexBuffer());

	// 頂点バッファの解析
	u_char* vertexAddress;
	DirectXCheck(xMesh_->LockVertexBuffer(
		D3DLOCK_READONLY, (void**)&vertexAddress));
	vertexAddress += (attribute.VertexStart * vertexSize_);
	u_int vertexCount = attribute.VertexCount;
	meshData->setVertexCount(vertexCount);
	Assert(positionOffset_ != -1);
	if(normalOffset_ != -1){ meshData->enableNormal(true); }
	if(uvOffset_ != -1){
		meshData->setTexCoordSetCount(1);
		meshData->setTexCoordType(0, TexCoord::type2);
	}
	for(u_int i = 0; i < vertexCount; i++){
		// 位置
		Vector3 position = *(Vector3*)(vertexAddress + positionOffset_);
		position.z = -position.z; // Z反転
		meshData->setPosition(i, position);
		// 法線
		if(normalOffset_ != -1){
			Vector3 normal = *(Vector3*)(vertexAddress + normalOffset_);
			normal.z = -normal.z; // Z反転
			meshData->setNormal(i, normal);
		}
		// UV
		if(uvOffset_ != -1){
			TexCoord2 uv = *(TexCoord2*)(vertexAddress + uvOffset_);
			meshData->setTexCoord2(i, 0, uv);
		}
		vertexAddress += vertexSize_;
	}
	DirectXCheck(xMesh_->UnlockVertexBuffer());
	return true;
}
//------------------------------------------------------------------------------
