//------------------------------------------------------------------------------
// 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/MeshData/MeshData.h"
#include "Graphics/MeshData/MeshDataManager.h"
#include "Graphics/Renderer/RenderingDevice.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
MeshData::MeshData(const String& name, Scene* scene) :
	SceneObject(name, scene), boundingSphere_(Sphere::zero),
	boundingBox_(AxisAlignedBox::zero), indexBuffer_(NULL),
	vertexDeclaration_(NULL), vertexSize_(0), vertexBuffer_(NULL),
	primitiveType_(Mesh::triangleList),
	vertexIndexCount_(0), vertexIndexArray_(NULL), vertexCount_(0),
	positions_(NULL), normals_(NULL), colors_(NULL),
	texCoordSetCount_(0), normalFlag_(false), colorFlag_(false),
	bonesPerVertex_(0), boneIndices_(NULL), weightsPerVertex_(0),
	weights_(NULL), vertexBufferChanged_(true), indexBufferChanged_(true),
	boundingChanged_(true){
	for(int i = 0; i < TexCoord::maxSetCount; i++){
		texCoordTypes_[i] = TexCoord::type2;
		texCoords_[i] = NULL;
	}
}
//------------------------------------------------------------------------------
// デストラクタ
MeshData::~MeshData(){
	SafeArrayDelete(weights_);
	SafeArrayDelete(boneIndices_);
	for(int i = 0; i < texCoordSetCount_; i++){
		SafeArrayDelete(texCoords_[i]);
	}
	SafeArrayDelete(colors_);
	SafeArrayDelete(normals_);
	SafeArrayDelete(positions_);
	SafeArrayDelete(vertexIndexArray_);
	SafeRelease(vertexBuffer_);
	SafeRelease(vertexDeclaration_);
	SafeRelease(indexBuffer_);
}
//------------------------------------------------------------------------------
// コピー
MeshData* MeshData::copy() const{
	MeshDataManager* manager = scene_->getMeshDataManager();
	MeshData* copyMeshData = manager->createMeshData(manager->rename(name_));
	// メッシュデータの値コピー
	copyMeshDataValue(copyMeshData);
	return copyMeshData;
}
//------------------------------------------------------------------------------
// 破棄
int MeshData::destroy(MeshData* meshData){
	Assert(meshData != NULL);
	// 引数の破棄
	MeshDataManager* manager = meshData->getScene()->getMeshDataManager();
	if(manager->destroy(meshData) == 0){ return 1; }
	return 0;
}
//------------------------------------------------------------------------------
// メッシュデータの値コピー
void MeshData::copyMeshDataValue(MeshData* destination) const{
	destination->setBoundingBox(getBoundingBox());
	destination->setBoundingSphere(getBoundingSphere());
	// プリミティブタイプ設定
	destination->setPrimitiveType(primitiveType_);
	// インデックスデータのコピー
	if(hasVertexIndices()){
		destination->setVertexIndexCount(vertexIndexCount_);
		std::memcpy(destination->vertexIndexArray_,
			vertexIndexArray_, sizeof(u_short) * vertexIndexCount_);
	}
	// 位置データのコピー
	destination->setVertexCount(vertexCount_);
	std::memcpy(destination->positions_,
		positions_, sizeof(Vector3) * vertexCount_);
	// 法線データのコピー
	destination->enableNormal(normalFlag_);
	if(normalFlag_){
		std::memcpy(destination->normals_,
			normals_, sizeof(Vector3) * vertexCount_);
	}
	// 色データのコピー
	destination->enableColor(colorFlag_);
	if(colorFlag_){
		std::memcpy(destination->colors_,
			colors_, sizeof(Color4c) * vertexCount_);
	}
	// テクスチャ座標のコピー
	destination->setTexCoordSetCount(texCoordSetCount_);
	for(int i = 0; i < texCoordSetCount_; i++){
		destination->setTexCoordType(i, texCoordTypes_[i]);
		std::memcpy(destination->texCoords_[i], texCoords_[i],
			sizeof(float) * texCoordTypes_[i] * vertexCount_);
	}
	// ボーンデータのコピー
	if(hasBoneIndex()){
		std::memcpy(destination->boneIndices_,
			boneIndices_, sizeof(u_char) * vertexCount_ * bonesPerVertex_);
	}
	// ウェイトデータのコピー
	if(hasWeight()){
		std::memcpy(destination->weights_,
			weights_, sizeof(float) * vertexCount_ * weightsPerVertex_);
	}
}
//------------------------------------------------------------------------------
// プリミティブタイプ
//------------------------------------------------------------------------------
// プリミティブタイプの設定
void MeshData::setPrimitiveType(Mesh::PrimitiveType primitiveType){
	primitiveType_ = primitiveType;
	// インデックス情報をクリア
	vertexIndexCount_ = 0;
	SafeArrayDelete(vertexIndexArray_);
	// インデックスバッファの解放
	SafeRelease(indexBuffer_);
}
//------------------------------------------------------------------------------
// プリミティブカウントの取得
int MeshData::getPrimitiveCount() const{
	if(primitiveType_ == Mesh::triangleList){
		Assert((getVertexCount() % 3) == 0);
		return (getVertexCount() / 3);
	}else if(primitiveType_ == Mesh::indexedTriangleList){
		Assert((getVertexIndexCount() % 3) == 0);
		return (getVertexIndexCount() / 3);
	}
	ErrorOut("MeshData::getPrimitiveCount() "
		"サポートされていないプリミティブタイプです");
	return 0;
}
//------------------------------------------------------------------------------
// 三角の取得
Triangle MeshData::getTriangle(int index) const{
	Triangle triangle;
	int offset = index * 3;
	if(primitiveType_ == Mesh::triangleList){
		triangle.setVertex(0, getPosition(offset + 0));
		triangle.setVertex(1, getPosition(offset + 1));
		triangle.setVertex(2, getPosition(offset + 2));
	}else if(primitiveType_ == Mesh::indexedTriangleList){
		triangle.setVertex(0, getPosition(getVertexIndex(offset + 0)));
		triangle.setVertex(1, getPosition(getVertexIndex(offset + 1)));
		triangle.setVertex(2, getPosition(getVertexIndex(offset + 2)));
	}else{
		ErrorOut("MeshData::getTriangle() "
			"サポートされていないプリミティブタイプです");
	}
	return triangle;
}
//------------------------------------------------------------------------------
// インデックス
//------------------------------------------------------------------------------
// 頂点インデックス数の設定
void MeshData::setVertexIndexCount(int vertexIndexCount){
	Assert(hasVertexIndices());
	if(vertexIndexCount_ == vertexIndexCount){ return; }
	vertexIndexCount_ = vertexIndexCount;
	SafeArrayDelete(vertexIndexArray_);
	if(vertexIndexCount_ == 0){ return; }
	vertexIndexArray_ = new u_short[vertexIndexCount_];
	// インデックスバッファの解放
	SafeRelease(indexBuffer_);
}
//------------------------------------------------------------------------------
// 頂点
//------------------------------------------------------------------------------
// 頂点数の設定
void MeshData::setVertexCount(int vertexCount){
	Assert(vertexCount >= 0);
	if(vertexCount_ == vertexCount){ return; }
	vertexCount_ = vertexCount;
	SafeArrayDelete(boneIndices_);
	SafeArrayDelete(weights_);
	for(int i = 0; i < texCoordSetCount_; i++){
		SafeArrayDelete(texCoords_[i]);
	}
	SafeArrayDelete(colors_);
	SafeArrayDelete(normals_);
	SafeArrayDelete(positions_);
	if(vertexCount_ == 0){ return; }
	positions_ = new Vector3[vertexCount_];
	if(normalFlag_){ normals_ = new Vector3[vertexCount_]; }
	if(colorFlag_){ colors_ = new Color4c[vertexCount_]; }
	for(int i = 0; i < texCoordSetCount_; i++){
		texCoords_[i] = new float[vertexCount_ * texCoordTypes_[i]];
	}
	if(hasBoneIndex()){
		boneIndices_ = new u_char[vertexCount_ * bonesPerVertex_];
	}
	if(hasWeight()){ weights_ = new float[vertexCount_ * weightsPerVertex_]; }
	// 頂点バッファを解放する
	SafeRelease(vertexBuffer_);
}
//------------------------------------------------------------------------------
// 法線を有効にするかどうか
void MeshData::enableNormal(bool normalFlag){
	if(normalFlag_ == normalFlag){ return; }
	normalFlag_ = normalFlag;
	SafeArrayDelete(normals_);
	if(normalFlag_ && (vertexCount_ != 0)){
		normals_ = new Vector3[vertexCount_];
	}
	// 頂点バッファを解放する
	SafeRelease(vertexBuffer_);
	// 頂点記述を解放する
	vertexSize_ = 0;
	SafeRelease(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// カラーを有効にするかどうか
void MeshData::enableColor(bool colorFlag){
	if(colorFlag_ == colorFlag){ return; }
	colorFlag_ = colorFlag;
	SafeArrayDelete(colors_);
	if(colorFlag_ && (vertexCount_ != 0)){
		colors_ = new Color4c[vertexCount_];
	}
	// 頂点バッファを解放する
	SafeRelease(vertexBuffer_);
	// 頂点記述を解放する
	vertexSize_ = 0;
	SafeRelease(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// テクスチャ座標セット数の設定
void MeshData::setTexCoordSetCount(int texCoordSetCount){
	if(texCoordSetCount_ == texCoordSetCount){ return; }
	for(int i = 0; i < texCoordSetCount; i++){ SafeArrayDelete(texCoords_[i]); }
	texCoordSetCount_ = texCoordSetCount;
	if(vertexCount_ == 0){ return; }
	for(int i = 0; i < texCoordSetCount_; i++){
		texCoords_[i] = new float[vertexCount_ * texCoordTypes_[i]];
	}
	// 頂点バッファを解放する
	SafeRelease(vertexBuffer_);
	// 頂点記述を解放する
	vertexSize_ = 0;
	SafeRelease(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// テクスチャ座標タイプの設定
void MeshData::setTexCoordType(int texCoordSet, TexCoord::Type texCoordType){
	Assert((texCoordSet >= 0) && (texCoordSet < texCoordSetCount_));
	if(texCoordTypes_[texCoordSet] == texCoordType){ return; }
	texCoordTypes_[texCoordSet] = texCoordType;
	SafeArrayDelete(texCoords_[texCoordSet]);
	if(vertexCount_ == 0){ return; }
	texCoords_[texCoordSet] =
		new float[vertexCount_ * texCoordTypes_[texCoordSet]];
	// 頂点バッファを解放する
	SafeRelease(vertexBuffer_);
	// 頂点記述を解放する
	vertexSize_ = 0;
	SafeRelease(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// 頂点あたりボーン数の設定
void MeshData::setBonesPerVertex(int bonesPerVertex){
	Assert(bonesPerVertex >= 0);
	Assert(bonesPerVertex <= 4);
	if(bonesPerVertex_ == bonesPerVertex){ return; }
	bonesPerVertex_ = bonesPerVertex;
	weightsPerVertex_ = (bonesPerVertex_ - 1);
	if(weightsPerVertex_ < 0){ weightsPerVertex_ = 0; }
	// 以前のデータを削除
	SafeArrayDelete(weights_);
	SafeArrayDelete(boneIndices_);
	if(vertexCount_ == 0){ return; }
	if(hasBoneIndex()){
		boneIndices_ = new u_char[vertexCount_ * bonesPerVertex_];
	}
	if(hasWeight()){
		weights_ = new float[vertexCount_ * weightsPerVertex_];
	}
	// 頂点バッファを解放する
	SafeRelease(vertexBuffer_);
	// 頂点記述を解放する
	vertexSize_ = 0;
	SafeRelease(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// グラフィックスバッファ
//------------------------------------------------------------------------------
// インデックスバッファの取得
Direct3DIndexBuffer* MeshData::getIndexBuffer(){
	// インデックスバッファを作成する
	if(indexBuffer_ == NULL){
		Assert(hasVertexIndices());
		Assert(getVertexIndexCount() != 0);
		RenderingDevice* device = RenderingDevice::getInstance();
		int bufferSize = getVertexIndexCount() * sizeof(u_short);
		indexBuffer_ = device->createDynamicIndexBuffer(bufferSize);
		indexBufferChanged_ = true;
	}
	// インデックスバッファに書き込む
	if(indexBufferChanged_){
		RenderingDevice* device = RenderingDevice::getInstance();
		int bufferSize = getVertexIndexCount() * sizeof(u_short);
		device->writeDynamicIndexBuffer(
			indexBuffer_, getVertexIndexArray(), bufferSize);
		indexBufferChanged_ = false;
	}
	return indexBuffer_;
}
//------------------------------------------------------------------------------
// 頂点記述の取得
Direct3DVertexDeclaration* MeshData::getVertexDeclaration(){
	// 頂点記述を作成する
	if(vertexDeclaration_ == NULL){
		Assert(vertexSize_ == 0);
		vertexSize_ = RenderingDevice::getInstance()->createVertexDeclaration(
			&vertexDeclaration_, true, getWeightsPerVertex(),
			getBonesPerVertex(), hasNormal(), hasColor(), getTexCoordSetCount(),
			getTexCoordTypeArray());
	}
	return vertexDeclaration_;
}
//------------------------------------------------------------------------------
// 頂点サイズの取得
int MeshData::getVertexSize(){
	getVertexDeclaration();
	return vertexSize_;
}
//------------------------------------------------------------------------------
// 頂点バッファの構築
Direct3DVertexBuffer* MeshData::getVertexBuffer(){
	RenderingDevice* device = RenderingDevice::getInstance();
	int vertexCount = getVertexCount();
	int bufferSize = getVertexSize() * vertexCount;
	// 頂点バッファを作成する
	if(vertexBuffer_ == NULL){
		vertexBuffer_ = device->createDynamicVertexBuffer(bufferSize);
		vertexBufferChanged_ = true;
	}
	// 頂点バッファに書き込む
	if(vertexBufferChanged_){
		device->writeDynamicVertexBuffer(vertexBuffer_, bufferSize, vertexCount,
			getPositionArray(), getWeightsPerVertex(), getWeightArray(),
			getBonesPerVertex(), getBoneIndexArray(), getNormalArray(),
			getColorArray(), getTexCoordSetCount(), getTexCoordTypeArray(),
			getTexCoordArray());
		vertexBufferChanged_ = false;
	}
	return vertexBuffer_;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
