//------------------------------------------------------------------------------
// 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/CharacterMesh.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Graphics/Scene/Scene.h"
#include "Graphics/Mesh/MeshManager.h"
#include "Graphics/Model/CharacterModel.h"
#include "Graphics/MeshData/MeshData.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
CharacterMesh::CharacterMesh(const String& name, Scene* scene) :
	Mesh(name, scene), vertexBuffer_(NULL), vertexDeclaration_(NULL),
	vertexSize_(0), deformedVertexCount_(0), deformedPosition_(NULL),
	deformedNormal_(NULL){
}
//------------------------------------------------------------------------------
// デストラクタ
CharacterMesh::~CharacterMesh(){
	SafeArrayDelete(deformedNormal_);
	SafeArrayDelete(deformedPosition_);
	SafeRelease(vertexBuffer_);
	SafeRelease(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// キャラクタメッシュのコピー
CharacterMesh* CharacterMesh::copyCharacterMesh(u_int copyMask) const{
	MeshManager* manager = scene_->getMeshManager();
	CharacterMesh* copyMesh =
		manager->createCharacterMesh(manager->rename(name_));
	// メッシュの値コピー
	copyMeshValue(copyMesh, copyMask);
	return copyMesh;
}
//------------------------------------------------------------------------------
// 走査
void CharacterMesh::traverse(const Matrix34& parentMatrix,
	bool parentEnabled, bool parentScaled, bool parentChanged){
	Mesh::traverse(parentMatrix, parentEnabled, parentScaled, parentChanged);
	if(!isGlobalEnabled()){ SafeRelease(vertexBuffer_); }
}
//------------------------------------------------------------------------------
// キャラクタ変形
//------------------------------------------------------------------------------
// キャラクタ変形
bool CharacterMesh::characterDeform(){
	// 各バッファ作成
// 一度VertexBufferが作られた後にMeshDataの頂点フォーマットに変更があるとおかしくなる
// 必要性は感じられないが、なんか対策うったほうがいいかも、メッシュデータから親たどる？
// きたないなぁ
	if(vertexDeclaration_ == NULL){ createVertexDeclaration(); }
	if(vertexBuffer_ == NULL){ createVertexBuffer(); }
	// 変形
	deform();
	// 頂点バッファセットアップ
	setupVertexBuffer();
	return true;
}
//------------------------------------------------------------------------------
// 変形
void CharacterMesh::deform(){
	// 変形行列配列取得
	CharacterModel* characterModel = getParent()->castCharacterModel();
	Assert(characterModel != NULL);
	// ボーン行列の構築。構築済みであれば再構築されない
	characterModel->buildBoneMatrix();
	const Matrix34* positionDeformMatrixArray =
		characterModel->getPositionDeformMatrixArray();

	// 変形データバッファ作成
	int vertexCount = getVertexCount();
	if((deformedPosition_ == NULL) || (deformedVertexCount_ != vertexCount)){
		SafeArrayDelete(deformedNormal_);
		SafeArrayDelete(deformedPosition_);
		deformedVertexCount_ = vertexCount;
		deformedPosition_ = new Vector3[deformedVertexCount_];
		if(hasNormal()){ deformedNormal_ = new Vector3[deformedVertexCount_]; }
	}

	// 変形
	if(!hasNormal()){
		// 位置のみ
		if(getBonesPerVertex() == 1){
			stitchingDeformP(positionDeformMatrixArray);
		}else{
			skinningDeformP(positionDeformMatrixArray);
		}
	}else if(!characterModel->isBoneScaled()){
		// 位置、法線
		if(getBonesPerVertex() == 1){
			stitchingDeformPN(positionDeformMatrixArray);
		}else{
			skinningDeformPN(positionDeformMatrixArray);
		}
	}else{
		// 位置、法線、スケールあり
		const Matrix33* normalDeformMatrixArray =
			characterModel->getNormalDeformMatrixArray();
		if(getBonesPerVertex() == 1){
			stitchingDeformPN(
				positionDeformMatrixArray, normalDeformMatrixArray);
		}else{
			skinningDeformPN(
				positionDeformMatrixArray, normalDeformMatrixArray);
		}
	}
}
//------------------------------------------------------------------------------
// スキニング変形
//------------------------------------------------------------------------------
// スキニング変形
void CharacterMesh::skinningDeformP(const Matrix34* positionDeformMatrixArray){
	int vertexCount = getVertexCount();
	int weightsPerVertex = getWeightsPerVertex();
	int bonesPerVertex = getBonesPerVertex();
	const float* weightArray = getWeightArray();
	const u_char* boneIndexArray = getBoneIndexArray();
	const Vector3* readPosition = getPositionArray();
	Vector3* writePosition = deformedPosition_;
	for(int i = 0; i < vertexCount; i++){
		int weightOffset = i * weightsPerVertex;
		int boneOffset = i * bonesPerVertex;
		Vector3 accumulatePosition(Vector3::zero);
		Vector3 accumulateNormal(Vector3::zero);
		float lastWeight = 1.f;
		for(int j = 0; j < weightsPerVertex; j++){
			// 変形前準備
			float weight = weightArray[weightOffset + j];
			lastWeight -= weight;
			if(weight <= Math::epsilon){ continue; }
			const Matrix34& positionDeformMatrix =
				positionDeformMatrixArray[boneIndexArray[boneOffset + j]];
			// 位置の変形
			accumulatePosition +=
				positionDeformMatrix * (*readPosition) * weight;
		}
		// 最後のボーン分の変形
		if(lastWeight > Math::epsilon){
			const Matrix34& positionDeformMatrix = positionDeformMatrixArray[
				boneIndexArray[boneOffset + weightsPerVertex]];
			// 位置の変形
			accumulatePosition +=
				positionDeformMatrix * (*readPosition) * lastWeight;
		}
		(*writePosition) = accumulatePosition;
		readPosition++;
		writePosition++;
	}
}
//------------------------------------------------------------------------------
// スキニング変形
void CharacterMesh::skinningDeformPN(const Matrix34* positionDeformMatrixArray){
	int vertexCount = getVertexCount();
	int weightsPerVertex = getWeightsPerVertex();
	int bonesPerVertex = getBonesPerVertex();
	const float* weightArray = getWeightArray();
	const u_char* boneIndexArray = getBoneIndexArray();
	const Vector3* readPosition = getPositionArray();
	const Vector3* readNormal = getNormalArray();
	Vector3* writePosition = deformedPosition_;
	Vector3* writeNormal = deformedNormal_;
	for(int i = 0; i < vertexCount; i++){
		int weightOffset = i * weightsPerVertex;
		int boneOffset = i * bonesPerVertex;
		Vector3 accumulatePosition(Vector3::zero);
		Vector3 accumulateNormal(Vector3::zero);
		float lastWeight = 1.f;
		for(int j = 0; j < weightsPerVertex; j++){
			// 変形前準備
			float weight = weightArray[weightOffset + j];
			lastWeight -= weight;
			if(weight <= Math::epsilon){ continue; }
			const Matrix34& positionDeformMatrix =
				positionDeformMatrixArray[boneIndexArray[boneOffset + j]];
			// 位置の変形
			accumulatePosition +=
				positionDeformMatrix * (*readPosition) * weight;
			// 法線の変形
			accumulateNormal +=
				positionDeformMatrix.multiply33(*readNormal) * weight;
		}
		// 最後のボーン分の変形
		if(lastWeight > Math::epsilon){
			const Matrix34& positionDeformMatrix = positionDeformMatrixArray[
				boneIndexArray[boneOffset + weightsPerVertex]];
			// 位置の変形
			accumulatePosition +=
				positionDeformMatrix * (*readPosition) * lastWeight;
			// 法線の変形
			accumulateNormal +=
				positionDeformMatrix.multiply33(*readNormal) * lastWeight;
		}
		(*writePosition) = accumulatePosition;
		readPosition++;
		writePosition++;
		(*writeNormal) = accumulateNormal;
		readNormal++;
		writeNormal++;
	}
}
//------------------------------------------------------------------------------
// スキニング変形
void CharacterMesh::skinningDeformPN(const Matrix34* positionDeformMatrixArray,
	const Matrix33* normalDeformMatrixArray){
	int vertexCount = getVertexCount();
	int weightsPerVertex = getWeightsPerVertex();
	int bonesPerVertex = getBonesPerVertex();
	const float* weightArray = getWeightArray();
	const u_char* boneIndexArray = getBoneIndexArray();
	const Vector3* readPosition = getPositionArray();
	const Vector3* readNormal = getNormalArray();
	bool calcNormal = hasNormal();
	Vector3* writePosition = deformedPosition_;
	Vector3* writeNormal = deformedNormal_;
	for(int i = 0; i < vertexCount; i++){
		int weightOffset = i * weightsPerVertex;
		int boneOffset = i * bonesPerVertex;
		Vector3 accumulatePosition(Vector3::zero);
		Vector3 accumulateNormal(Vector3::zero);
		float lastWeight = 1.f;
		for(int j = 0; j < weightsPerVertex; j++){
			// 変形前準備
			float weight = weightArray[weightOffset + j];
			lastWeight -= weight;
			if(weight <= Math::epsilon){ continue; }
			// 位置の変形
			const Matrix34& positionDeformMatrix =
				positionDeformMatrixArray[boneIndexArray[boneOffset + j]];
			accumulatePosition +=
				positionDeformMatrix * (*readPosition) * weight;
			// 法線の変形
			const Matrix33& normalDeformMatrix =
				normalDeformMatrixArray[boneIndexArray[boneOffset + j]];
			accumulateNormal += normalDeformMatrix * (*readNormal) * weight;
		}
		// 最後のボーン分の変形
		if(lastWeight > Math::epsilon){
			// 位置の変形
			const Matrix34& positionDeformMatrix = positionDeformMatrixArray[
				boneIndexArray[boneOffset + weightsPerVertex]];
			accumulatePosition +=
				positionDeformMatrix * (*readPosition) * lastWeight;
			// 法線の変形
			const Matrix33& normalDeformMatrix = normalDeformMatrixArray[
				boneIndexArray[boneOffset + weightsPerVertex]];
			accumulateNormal +=
				normalDeformMatrix * (*readNormal) * lastWeight;
		}
		(*writePosition) = accumulatePosition;
		readPosition++;
		writePosition++;
		(*writeNormal) = accumulateNormal;
		readNormal++;
		writeNormal++;
	}
}
//------------------------------------------------------------------------------
// スティッチング変形
//------------------------------------------------------------------------------
// スティッチング変形
void CharacterMesh::stitchingDeformP(const Matrix34* positionDeformMatrixArray){
	int vertexCount = getVertexCount();
	const u_char* boneIndexArray = getBoneIndexArray();
	const Vector3* readPosition = getPositionArray();
	Vector3* writePosition = deformedPosition_;
	for(int i = 0; i < vertexCount; i++){
		const Matrix34& positionDeformMatrix =
			positionDeformMatrixArray[boneIndexArray[i]];
		// 位置の変形
		(*writePosition) = positionDeformMatrix * (*readPosition);
		readPosition++;
		writePosition++;
	}
}
//------------------------------------------------------------------------------
// スティッチング変形
void CharacterMesh::stitchingDeformPN(
	const Matrix34* positionDeformMatrixArray){
	int vertexCount = getVertexCount();
	const u_char* boneIndexArray = getBoneIndexArray();
	const Vector3* readPosition = getPositionArray();
	const Vector3* readNormal = getNormalArray();
	Vector3* writePosition = deformedPosition_;
	Vector3* writeNormal = deformedNormal_;
	for(int i = 0; i < vertexCount; i++){
		const Matrix34& positionDeformMatrix =
			positionDeformMatrixArray[boneIndexArray[i]];
		// 位置の変形
		(*writePosition) = positionDeformMatrix * (*readPosition);
		readPosition++;
		writePosition++;
		// 法線の変形
		(*writeNormal) = positionDeformMatrix.multiply33(*readNormal);
		readNormal++;
		writeNormal++;
	}
}
//------------------------------------------------------------------------------
// スティッチング変形
void CharacterMesh::stitchingDeformPN(const Matrix34* positionDeformMatrixArray,
	const Matrix33* normalDeformMatrixArray){
	int vertexCount = getVertexCount();
	const u_char* boneIndexArray = getBoneIndexArray();
	const Vector3* readPosition = getPositionArray();
	const Vector3* readNormal = getNormalArray();
	Vector3* writePosition = deformedPosition_;
	Vector3* writeNormal = deformedNormal_;
	for(int i = 0; i < vertexCount; i++){
		const Matrix34& positionDeformMatrix =
			positionDeformMatrixArray[boneIndexArray[i]];
		const Matrix33& normalDeformMatrix =
			normalDeformMatrixArray[boneIndexArray[i]];
		// 位置の変形
		(*writePosition) = positionDeformMatrix * (*readPosition);
		readPosition++;
		writePosition++;
		// 法線の変形
		(*writeNormal) = normalDeformMatrix * (*readNormal);
		readNormal++;
		writeNormal++;
	}
}
//------------------------------------------------------------------------------
// 頂点バッファセットアップ
//------------------------------------------------------------------------------
// 頂点バッファセットアップ
bool CharacterMesh::setupVertexBuffer(){
	// 動的頂点バッファの書き込み
	Assert(vertexDeclaration_ != NULL);
	Assert(vertexBuffer_ != NULL);
	RenderingDevice* device = RenderingDevice::getInstance();
	int vertexCount = getVertexCount();
	// 変形した位置と法線を使用し、WeightとBoneIndexを使用しない
	device->writeDynamicVertexBuffer(vertexBuffer_, vertexSize_ * vertexCount,
		vertexCount, deformedPosition_, 0, NULL, 0, NULL, deformedNormal_,
		getColorArray(), getTexCoordSetCount(), getTexCoordTypeArray(),
		getTexCoordArray());
	return true;
}
//------------------------------------------------------------------------------
// グラフィックスバッファ
//------------------------------------------------------------------------------
// 頂点記述の構築
bool CharacterMesh::createVertexDeclaration(){
	Assert(vertexDeclaration_ == NULL);
	Assert(vertexSize_ == 0);
	// ソフトウェアでキャラクタ変形を行うのでWeightとBoneIndexを0にする
	RenderingDevice* device = RenderingDevice::getInstance();
	vertexSize_ = device->createVertexDeclaration(
		&vertexDeclaration_, true, 0, 0, hasNormal(), hasColor(),
		getTexCoordSetCount(), getTexCoordTypeArray());
	return true;
}
//------------------------------------------------------------------------------
// 頂点バッファの構築
bool CharacterMesh::createVertexBuffer(){
	Assert(vertexBuffer_ == NULL);
	// 先に頂点記述を構築しておく必要がある
	if(vertexDeclaration_ == NULL){ createVertexDeclaration(); }
	Assert(vertexDeclaration_ != NULL);
	// 動的頂点バッファの作成
	RenderingDevice* device = RenderingDevice::getInstance();
	int vertexCount = getVertexCount();
	u_int bufferSize = vertexSize_ * vertexCount;
	vertexBuffer_ = device->createDynamicVertexBuffer(bufferSize);
	return true;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
