//------------------------------------------------------------------------------
// 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
 * メッシュ実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Graphics/Mesh/Mesh.h"
#include "Graphics/Mesh/MeshManager.h"
#include "Graphics/Model/Model.h"
#include "Graphics/MeshData/MeshData.h"
#include "Graphics/Material/Material.h"

namespace Lamp{

// プリミティブタイプ文字列テーブル
const String Mesh::primitiveTypeStringTable[] = {
	"TriangleList",
	"IndexedTriangleList",
};

//------------------------------------------------------------------------------
// コンストラクタ
Mesh::Mesh(const String& name, Scene* scene) : SceneObject(name, scene),
	parent_(NULL), meshData_(NULL), material_(NULL),
	worldBoundingSphere_(Sphere::zero), worldBoundingBox_(AxisAlignedBox::zero),
	renderingTemporaryData_(0.f), enabled_(true), globalEnabled_(false){
}
//------------------------------------------------------------------------------
// デストラクタ
Mesh::~Mesh(){
}
//------------------------------------------------------------------------------
// メッシュの値コピー
void Mesh::copyMeshValue(Mesh* destination, u_int copyMask) const{
	// メンバのコピー
	destination->setEnabled(enabled_);
	// メッシュデータ
	if(meshData_ != NULL){
		if((copyMask & copyMeshData) == 0){
			// メッシュデータを共有する
			destination->setMeshData(meshData_);
		}else{
			// メッシュデータをコピーする
			destination->setMeshData(meshData_->copy());
		}
	}
	// マテリアル
	if(material_ != NULL){
		if((copyMask & copyMaterial) == 0){
			// マテリアルを共有する
			destination->setMaterial(material_);
		}else{
			// マテリアルをコピーする
			destination->setMaterial(material_->copy(copyMask));
		}
	}
}
//------------------------------------------------------------------------------
// 再帰的破棄
int Mesh::recursiveDestroy(Mesh* mesh){
	Assert(mesh != NULL);
	int result = 0;
	// メッシュデータの破棄
	MeshData* meshData = mesh->getMeshData();
	if(meshData != NULL){
		mesh->setMeshData(NULL);
		result += MeshData::destroy(meshData);
	}
	// マテリアルの破棄
	Material* material = mesh->getMaterial();
	if(material != NULL){
		mesh->setMaterial(NULL);
		result += Material::recursiveDestroy(material);
	}
	// 親から削除
	Model* parent = mesh->getParent();
	if(parent != NULL){ parent->removeMesh(mesh); }
	// 引数の破棄
	MeshManager* manager = mesh->getScene()->getMeshManager();
	if(manager->destroy(mesh) == 0){ result++; }
	return result;
}
//------------------------------------------------------------------------------
// 走査
void Mesh::traverse(const Matrix34& parentMatrix,
	bool parentEnabled, bool parentScaled, bool parentChanged){
	globalEnabled_ = parentEnabled && isEnabled();
	// parentEnabled(false) && isEnabled(true)の場合はバウンディングを計算する
	if(!isEnabled()){ return; }
	globalScaled_ = parentScaled;
	// 親に変更があったか、バウンディングに変更があれば再計算
	if(parentChanged || meshData_->isBoundingChanged()){
		// バウンディングボックス算出
		worldBoundingBox_ = getBoundingBox().transform(parentMatrix);
		if(globalScaled_){
			worldBoundingSphere_ =
				getBoundingSphere().scaledTransform(parentMatrix);
		}else{
			worldBoundingSphere_ = getBoundingSphere().transform(parentMatrix);
		}
		meshData_->clearBoundingChanged();
	}
}
//------------------------------------------------------------------------------
// バウンディングボックス
//------------------------------------------------------------------------------
// バウンディングボックスの設定
void Mesh::setBoundingBox(const AxisAlignedBox& boundingBox){
	Assert(meshData_ != NULL);
	meshData_->setBoundingBox(boundingBox);
}
//------------------------------------------------------------------------------
// バウンディングボックスの取得
const AxisAlignedBox& Mesh::getBoundingBox() const{
	Assert(meshData_ != NULL);
	return meshData_->getBoundingBox();
}
//------------------------------------------------------------------------------
// バウンディングスフィア
//------------------------------------------------------------------------------
// バウンディングスフィアの設定
void Mesh::setBoundingSphere(const Sphere& boundingSphere){
	Assert(meshData_ != NULL);
	meshData_->setBoundingSphere(boundingSphere);
}
//------------------------------------------------------------------------------
// バウンディングスフィアの取得
const Sphere& Mesh::getBoundingSphere() const{
	Assert(meshData_ != NULL);
	return meshData_->getBoundingSphere();
}
//------------------------------------------------------------------------------
// 中心の取得
const Vector3& Mesh::getCenter() const{
	Assert(meshData_ != NULL);
	return meshData_->getBoundingSphere().getCenter();
}
//------------------------------------------------------------------------------
// メッシュデータインターフェース
//------------------------------------------------------------------------------
// メッシュデータの設定
void Mesh::setMeshData(MeshData* meshData){
	if(meshData_ != NULL){ meshData_->removeReference(this); }
	meshData_ = meshData;
	if(meshData_ != NULL){ meshData_->addReference(this); }
}
//------------------------------------------------------------------------------
// マテリアルインターフェース
//------------------------------------------------------------------------------
// マテリアルの設定
void Mesh::setMaterial(Material* material){
	if(material_ != NULL){ material_->removeReference(this); }
	material_ = material;
	if(material_ != NULL){ material_->addReference(this); }
}
//------------------------------------------------------------------------------
// プリミティブタイプ
//------------------------------------------------------------------------------
// プリミティブタイプがインデックスを持つかどうか
bool Mesh::primitiveTypeHasIndex(PrimitiveType primitiveType){
	if(primitiveType == Mesh::indexedTriangleList){
		return true;
	}
	return false;
}
//------------------------------------------------------------------------------
// プリミティブタイプから文字列への変換
String Mesh::primitiveTypeToString(PrimitiveType primitiveType){
	Assert(primitiveType >= 0);
	Assert(primitiveType < ptMax);
	return primitiveTypeStringTable[primitiveType];
}
//------------------------------------------------------------------------------
// 文字列からプリミティブタイプへの変換
Mesh::PrimitiveType Mesh::primitiveTypeFromString(
	const String& primitiveTypeString){
	for(int i = 0; i < ptMax; i++){
		if(primitiveTypeStringTable[i].equals(primitiveTypeString)){
			return PrimitiveType(i);
		}
	}
	ErrorOut("Mesh::primitiveTypeFromString() " + primitiveTypeString);
	return ptMax;
}
//-----------------------------------------------------------------------------
// メッシュインターフェース
//-----------------------------------------------------------------------------
// プリミティブタイプの設定
void Mesh::setPrimitiveType(Mesh::PrimitiveType primitiveType){
	getMeshData()->setPrimitiveType(primitiveType);
}
//-----------------------------------------------------------------------------
// プリミティブタイプの取得
Mesh::PrimitiveType Mesh::getPrimitiveType() const{
	return getMeshData()->getPrimitiveType();
}
//-----------------------------------------------------------------------------
// プリミティブカウントの取得
int Mesh::getPrimitiveCount() const{
	return getMeshData()->getPrimitiveCount();
}
//-----------------------------------------------------------------------------
// 三角の取得
Triangle Mesh::getTriangle(int index) const{
	return getMeshData()->getTriangle(index);
}
//-----------------------------------------------------------------------------
// 頂点インデックスを持つかどうか
bool Mesh::hasVertexIndices() const{
	return getMeshData()->hasVertexIndices();
}
//-----------------------------------------------------------------------------
// 頂点インデックス数の設定
void Mesh::setVertexIndexCount(int vertexIndexCount){
	getMeshData()->setVertexIndexCount(vertexIndexCount);
}
//-----------------------------------------------------------------------------
// 頂点インデックス数の取得
int Mesh::getVertexIndexCount() const{
	return getMeshData()->getVertexIndexCount();
}
//-----------------------------------------------------------------------------
// 頂点インデックスの設定
void Mesh::setVertexIndex(int index, int vertexIndex){
	getMeshData()->setVertexIndex(index, vertexIndex);
}
//-----------------------------------------------------------------------------
// 頂点インデックスの取得
int Mesh::getVertexIndex(int index) const{
	return getMeshData()->getVertexIndex(index);
}
//-----------------------------------------------------------------------------
// 頂点インデックス配列の取得
const u_short* Mesh::getVertexIndexArray(){
	return getMeshData()->getVertexIndexArray();
}
//-----------------------------------------------------------------------------
// 頂点数の設定
void Mesh::setVertexCount(int vertexCount){
	getMeshData()->setVertexCount(vertexCount);
}
//-----------------------------------------------------------------------------
// 頂点数の取得
int Mesh::getVertexCount() const{ return getMeshData()->getVertexCount(); }
//-----------------------------------------------------------------------------
// 位置の設定
void Mesh::setPosition(int index, const Vector3& position){
	getMeshData()->setPosition(index, position);
}
//-----------------------------------------------------------------------------
// 位置の取得
const Vector3& Mesh::getPosition(int index) const{
	return getMeshData()->getPosition(index);
}
//-----------------------------------------------------------------------------
// 位置配列の取得
const Vector3* Mesh::getPositionArray() const{
	return getMeshData()->getPositionArray();
}
//-----------------------------------------------------------------------------
// 法線を有効にするかどうか
void Mesh::enableNormal(bool normalFlag){
	getMeshData()->enableNormal(normalFlag);
}
//-----------------------------------------------------------------------------
// 法線が有効かどうか
bool Mesh::hasNormal() const{ return getMeshData()->hasNormal(); }
//-----------------------------------------------------------------------------
// 法線の設定
void Mesh::setNormal(int index, const Vector3& normal){
	getMeshData()->setNormal(index, normal);
}
//-----------------------------------------------------------------------------
// 法線の取得
const Vector3& Mesh::getNormal(int index) const{
	return getMeshData()->getNormal(index);
}
//-----------------------------------------------------------------------------
// 法線配列の取得
const Vector3* Mesh::getNormalArray() const{
	return getMeshData()->getNormalArray();
}
//-----------------------------------------------------------------------------
// カラーを有効にするかどうか
void Mesh::enableColor(bool colorFlag){
	getMeshData()->enableColor(colorFlag);
}
//-----------------------------------------------------------------------------
// カラーが有効かどうか
bool Mesh::hasColor() const{ return getMeshData()->hasColor(); }
//-----------------------------------------------------------------------------
// カラーの設定
void Mesh::setColor(int index, const Color4c& color){
	getMeshData()->setColor(index, color);
}
//-----------------------------------------------------------------------------
// カラーの取得
const Color4c& Mesh::getColor(int index) const{
	return getMeshData()->getColor(index);
}
//-----------------------------------------------------------------------------
// カラー配列の取得
const Color4c* Mesh::getColorArray() const{
	return getMeshData()->getColorArray();
}
//-----------------------------------------------------------------------------
// テクスチャ座標セット数の設定
void Mesh::setTexCoordSetCount(int texCoordSetCount){
	getMeshData()->setTexCoordSetCount(texCoordSetCount);
}
//-----------------------------------------------------------------------------
// テクスチャ座標セット数の設定
int Mesh::getTexCoordSetCount() const{
	return getMeshData()->getTexCoordSetCount();
}
//-----------------------------------------------------------------------------
// テクスチャ座標タイプの設定
void Mesh::setTexCoordType(int texCoordSet, TexCoord::Type texCoordType){
	getMeshData()->setTexCoordType(texCoordSet, texCoordType);
}
//-----------------------------------------------------------------------------
// テクスチャ座標タイプの取得
TexCoord::Type Mesh::getTexCoordType(int texCoordSet) const{
	return getMeshData()->getTexCoordType(texCoordSet);
}
//-----------------------------------------------------------------------------
// テクスチャ座標タイプ配列の取得
const TexCoord::Type* Mesh::getTexCoordTypeArray() const{
	return getMeshData()->getTexCoordTypeArray();
}
//-----------------------------------------------------------------------------
// テクスチャ座標の設定
void Mesh::setTexCoord(
	int index, int texCoordSet, const float* texCoord, int numTexCoord){
	getMeshData()->setTexCoord(index, texCoordSet, texCoord, numTexCoord);
}
//-----------------------------------------------------------------------------
// テクスチャ座標配列の取得
const float* const* Mesh::getTexCoordArray() const{
	return getMeshData()->getTexCoordArray();
}
//-----------------------------------------------------------------------------
// テクスチャ座標配列の取得
const float* Mesh::getTexCoordArray(int texCoordSet) const{
	return getMeshData()->getTexCoordArray(texCoordSet);
}
//-----------------------------------------------------------------------------
// テクスチャ座標配列サイズの取得
int Mesh::getTexCoordArraySize(int texCoordSet) const{
	return getMeshData()->getTexCoordArraySize(texCoordSet);
}
//-----------------------------------------------------------------------------
// 一次元テクスチャ座標の設定
void Mesh::setTexCoord1(int index, int texCoordSet, const TexCoord1& texCoord){
	getMeshData()->setTexCoord1(index, texCoordSet, texCoord);
}
//-----------------------------------------------------------------------------
// 一次元テクスチャ座標の取得
const TexCoord1& Mesh::getTexCoord1(int index, int texCoordSet) const{
	return getMeshData()->getTexCoord1(index, texCoordSet);
}
//-----------------------------------------------------------------------------
// 一次元テクスチャ座標配列の取得
const TexCoord1* Mesh::getTexCoord1Array(int texCoordSet) const{
	return getMeshData()->getTexCoord1Array(texCoordSet);
}
//-----------------------------------------------------------------------------
// 二次元テクスチャ座標の設定
void Mesh::setTexCoord2(int index, int texCoordSet, const TexCoord2& texCoord){
	getMeshData()->setTexCoord2(index, texCoordSet, texCoord);
}
//-----------------------------------------------------------------------------
// 二次元テクスチャ座標の取得
const TexCoord2& Mesh::getTexCoord2(int index, int texCoordSet) const{
	return getMeshData()->getTexCoord2(index, texCoordSet);
}
//-----------------------------------------------------------------------------
// 二次元テクスチャ座標配列の取得
const TexCoord2* Mesh::getTexCoord2Array(int texCoordSet) const{
	return getMeshData()->getTexCoord2Array(texCoordSet);
}
//-----------------------------------------------------------------------------
// 三次元テクスチャ座標の設定
void Mesh::setTexCoord3(int index, int texCoordSet, const TexCoord3& texCoord){
	getMeshData()->setTexCoord3(index, texCoordSet, texCoord);
}
//-----------------------------------------------------------------------------
// 三次元テクスチャ座標の取得
const TexCoord3& Mesh::getTexCoord3(int index, int texCoordSet) const{
	return getMeshData()->getTexCoord3(index, texCoordSet);
}
//-----------------------------------------------------------------------------
// 三次元テクスチャ座標配列の取得
const TexCoord3* Mesh::getTexCoord3Array(int texCoordSet) const{
	return getMeshData()->getTexCoord3Array(texCoordSet);
}
//-----------------------------------------------------------------------------
// 四次元テクスチャ座標の設定
void Mesh::setTexCoord4(int index, int texCoordSet, const TexCoord4& texCoord){
	getMeshData()->setTexCoord4(index, texCoordSet, texCoord);
}
//-----------------------------------------------------------------------------
// 四次元テクスチャ座標の取得
const TexCoord4& Mesh::getTexCoord4(int index, int texCoordSet) const{
	return getMeshData()->getTexCoord4(index, texCoordSet);
}
//-----------------------------------------------------------------------------
// 四次元テクスチャ座標配列の取得
const TexCoord4* Mesh::getTexCoord4Array(int texCoordSet) const{
	return getMeshData()->getTexCoord4Array(texCoordSet);
}
//-----------------------------------------------------------------------------
// 頂点あたりボーン数の設定
void Mesh::setBonesPerVertex(int bonesPerVertex){
	getMeshData()->setBonesPerVertex(bonesPerVertex);
}
//-----------------------------------------------------------------------------
// 頂点あたりボーン数の取得
int Mesh::getBonesPerVertex() const{
	return getMeshData()->getBonesPerVertex();
}
//-----------------------------------------------------------------------------
// ボーンインデックスが有効かどうか
bool Mesh::hasBoneIndex() const{ return getMeshData()->hasBoneIndex(); }
//-----------------------------------------------------------------------------
// ボーンインデックスの設定
void Mesh::setBoneIndex(
	int vertexIndex, int weightIndex, u_char boneIndex){
	getMeshData()->setBoneIndex(
		vertexIndex, weightIndex, boneIndex);
}
//-----------------------------------------------------------------------------
// ボーンインデックスの設定
void Mesh::setBoneIndex(int vertexIndex, u_char boneIndex){
	getMeshData()->setBoneIndex(vertexIndex, boneIndex);
}
//-----------------------------------------------------------------------------
// ボーンインデックスの取得
u_char Mesh::getBoneIndex(int vertexIndex, int weightIndex) const{
	return getMeshData()->getBoneIndex(vertexIndex, weightIndex);
}
//-----------------------------------------------------------------------------
// ボーンインデックスの取得
u_char Mesh::getBoneIndex(int vertexIndex) const{
	return getMeshData()->getBoneIndex(vertexIndex);
}
//-----------------------------------------------------------------------------
// ボーンインデックス配列の取得
const u_char* Mesh::getBoneIndexArray() const{
	return getMeshData()->getBoneIndexArray();
}
//-----------------------------------------------------------------------------
// 頂点当たりウェイト数の取得
int Mesh::getWeightsPerVertex() const{
	return getMeshData()->getWeightsPerVertex();
}
//-----------------------------------------------------------------------------
// ウェイトが有効かどうか
bool Mesh::hasWeight() const{ return getMeshData()->hasWeight(); }
//-----------------------------------------------------------------------------
// ウェイトの設定
void Mesh::setWeight(int vertexIndex, int weightIndex, float weight){
	getMeshData()->setWeight(vertexIndex, weightIndex, weight);
}
//-----------------------------------------------------------------------------
// ウェイトの取得
float Mesh::getWeight(int vertexIndex, int weightIndex) const{
	return getMeshData()->getWeight(vertexIndex, weightIndex);
}
//-----------------------------------------------------------------------------
// ウェイト配列の取得
const float* Mesh::getWeightArray() const{
	return getMeshData()->getWeightArray();
}
//-----------------------------------------------------------------------------
// バッファアクセス
//-----------------------------------------------------------------------------
// インデックスバッファの取得
Direct3DIndexBuffer* Mesh::getIndexBuffer(){
	return getMeshData()->getIndexBuffer();
}
//-----------------------------------------------------------------------------
// 頂点記述の取得
Direct3DVertexDeclaration* Mesh::getVertexDeclaration(){
	return getMeshData()->getVertexDeclaration();
}
//-----------------------------------------------------------------------------
// 頂点サイズの取得
int Mesh::getVertexSize(){ return getMeshData()->getVertexSize(); }
//-----------------------------------------------------------------------------
// 頂点バッファの構築
Direct3DVertexBuffer* Mesh::getVertexBuffer(){
	return getMeshData()->getVertexBuffer();
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
