//------------------------------------------------------------------------------
// 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/PrimitiveRenderer/PrimitiveDrawRequestBuilder.h"
#include "Graphics/PrimitiveRenderer/PrimitiveDrawRequest.h"
#include "Graphics/Model/CharacterModel.h"

namespace Lamp{

//------------------------------------------------------------------------------
// ポイントの構築
void PrimitiveDrawRequestBuilder::buildPoint(PrimitiveDrawRequest* request){
	request->setVertexCount(12);
	request->enableColor(true);
	// X軸
	request->setPosition(0, Vector3::zero);
	request->setColor(0, Color4c(255, 255, 255, 255));
	request->setPosition(1, Vector3::unitX);
	request->setColor(1, Color4c(255, 255, 255, 64));
	request->setPosition(2, Vector3::zero);
	request->setColor(2, Color4c(255, 255, 255, 255));
	request->setPosition(3, -Vector3::unitX);
	request->setColor(3, Color4c(255, 255, 255, 64));
	// Y軸
	request->setPosition(4, Vector3::zero);
	request->setColor(4, Color4c(255, 255, 255, 255));
	request->setPosition(5, Vector3::unitY);
	request->setColor(5, Color4c(255, 255, 255, 64));
	request->setPosition(6, Vector3::zero);
	request->setColor(6, Color4c(255, 255, 255, 255));
	request->setPosition(7, -Vector3::unitY);
	request->setColor(7, Color4c(255, 255, 255, 64));
	// Z軸
	request->setPosition(8, Vector3::zero);
	request->setColor(8, Color4c(255, 255, 255, 255));
	request->setPosition(9, Vector3::unitZ);
	request->setColor(9, Color4c(255, 255, 255, 64));
	request->setPosition(10, Vector3::zero);
	request->setColor(10, Color4c(255, 255, 255, 255));
	request->setPosition(11, -Vector3::unitZ);
	request->setColor(11, Color4c(255, 255, 255, 64));
}
//------------------------------------------------------------------------------
// 軸ポイントの構築
void PrimitiveDrawRequestBuilder::buildAxisPoint(PrimitiveDrawRequest* request){
	request->setVertexCount(12);
	request->enableColor(true);
	// X軸
	request->setPosition(0, Vector3::zero);
	request->setColor(0, Color4c(255, 0, 0, 255));
	request->setPosition(1, Vector3::unitX);
	request->setColor(1, Color4c(255, 0, 0, 64));
	request->setPosition(2, Vector3::zero);
	request->setColor(2, Color4c(255, 0, 0, 255));
	request->setPosition(3, -Vector3::unitX);
	request->setColor(3, Color4c(255, 0, 0, 64));
	// Y軸
	request->setPosition(4, Vector3::zero);
	request->setColor(4, Color4c(0, 255, 0, 255));
	request->setPosition(5, Vector3::unitY);
	request->setColor(5, Color4c(0, 255, 0, 64));
	request->setPosition(6, Vector3::zero);
	request->setColor(6, Color4c(0, 255, 0, 255));
	request->setPosition(7, -Vector3::unitY);
	request->setColor(7, Color4c(0, 255, 0, 64));
	// Z軸
	request->setPosition(8, Vector3::zero);
	request->setColor(8, Color4c(0, 0, 255, 255));
	request->setPosition(9, Vector3::unitZ);
	request->setColor(9, Color4c(0, 0, 255, 64));
	request->setPosition(10, Vector3::zero);
	request->setColor(10, Color4c(0, 0, 255, 255));
	request->setPosition(11, -Vector3::unitZ);
	request->setColor(11, Color4c(0, 0, 255, 64));
}
//------------------------------------------------------------------------------
// 軸の構築
void PrimitiveDrawRequestBuilder::buildAxis(PrimitiveDrawRequest* request){
	request->setVertexCount(6);
	request->enableColor(true);
	// X軸
	request->setPosition(0, Vector3::zero);
	request->setColor(0, Color4c(255, 0, 0, 255));
	request->setPosition(1, Vector3::unitX);
	request->setColor(1, Color4c(255, 0, 0, 64));
	// Y軸
	request->setPosition(2, Vector3::zero);
	request->setColor(2, Color4c(0, 255, 0, 255));
	request->setPosition(3, Vector3::unitY);
	request->setColor(3, Color4c(0, 255, 0, 64));
	// Z軸
	request->setPosition(4, Vector3::zero);
	request->setColor(4, Color4c(0, 0, 255, 255));
	request->setPosition(5, Vector3::unitZ);
	request->setColor(5, Color4c(0, 0, 255, 64));
}
//------------------------------------------------------------------------------
// 矢印の構築
void PrimitiveDrawRequestBuilder::buildArrow(PrimitiveDrawRequest* request){
	request->setVertexCount(10);
	request->enableColor(true);
	request->setPosition(0, Vector3::zero);
	request->setColor(0, Color4c(255, 255, 255, 255));
	request->setPosition(1, Vector3::unitZ);
	request->setColor(1, Color4c(255, 255, 255, 255));

	float width = 0.25f;
	request->setPosition(2, Vector3::unitZ);
	request->setColor(2, Color4c(255, 255, 255, 255));
	request->setPosition(3, Vector3(width, width, 0.5f));
	request->setColor(3, Color4c(255, 255, 255, 64));

	request->setPosition(4, Vector3::unitZ);
	request->setColor(4, Color4c(255, 255, 255, 255));
	request->setPosition(5, Vector3(width, -width, 0.5f));
	request->setColor(5, Color4c(255, 255, 255, 64));

	request->setPosition(6, Vector3::unitZ);
	request->setColor(6, Color4c(255, 255, 255, 255));
	request->setPosition(7, Vector3(-width, width, 0.5f));
	request->setColor(7, Color4c(255, 255, 255, 64));

	request->setPosition(8, Vector3::unitZ);
	request->setColor(8, Color4c(255, 255, 255, 255));
	request->setPosition(9, Vector3(-width, -width, 0.5f));
	request->setColor(9, Color4c(255, 255, 255, 64));

}
//------------------------------------------------------------------------------
// グリッドの構築
void PrimitiveDrawRequestBuilder::buildGrid(
	PrimitiveDrawRequest* request, int division, int subDivision){
	int totalDivision = division * subDivision;
	int lineCount = totalDivision + 1;
	request->setVertexCount(lineCount * 4);
	request->enableColor(true);
	float scale = 1.f / totalDivision;
	for(int i = 0; i < lineCount; i++){
		int offset = i * 4;
		float value = i * scale - 0.5f;
		request->setPosition(offset + 0, Vector3(value, 0.f, -0.5f));
		request->setPosition(offset + 1, Vector3(value, 0.f, 0.5f));
		request->setPosition(offset + 2, Vector3(-0.5f, 0.f, value));
		request->setPosition(offset + 3, Vector3(0.5f, 0.f, value));
		Color4c color;
		if((i % subDivision) == 0){ color.set(255, 255, 255, 255); }
		else{ color.set(255, 255, 255, 64); }
		for(int j = 0; j < 4; j++){ request->setColor(offset + j,color); }
	}
}
//------------------------------------------------------------------------------
// 平面の構築
void PrimitiveDrawRequestBuilder::buildPlane(PrimitiveDrawRequest* request){
	request->setVertexCount(10);
	request->enableColor(true);
	request->setPosition(0, Vector3(0.f, 0.f, 0.f));
	request->setColor(0, Color4c(255, 255, 255, 255));
	request->setPosition(1, Vector3(0.f, 0.f, 1.f));
	request->setColor(1, Color4c(255, 255, 255, 192));

	request->setPosition(2, Vector3(0.5f, 0.5f, 0.f));
	request->setColor(2, Color4c(255, 255, 255, 192));
	request->setPosition(3, Vector3(-0.5f, 0.5f, 0.f));
	request->setColor(3, Color4c(255, 255, 255, 192));

	request->setPosition(4, Vector3(-0.5f, 0.5f, 0.f));
	request->setColor(4, Color4c(255, 255, 255, 192));
	request->setPosition(5, Vector3(-0.5f, -0.5f, 0.f));
	request->setColor(5, Color4c(255, 255, 255, 192));

	request->setPosition(6, Vector3(-0.5f, -0.5f, 0.f));
	request->setColor(6, Color4c(255, 255, 255, 192));
	request->setPosition(7, Vector3(0.5f, -0.5f, 0.f));
	request->setColor(7, Color4c(255, 255, 255, 192));

	request->setPosition(8, Vector3(0.5f, -0.5f, 0.f));
	request->setColor(8, Color4c(255, 255, 255, 192));
	request->setPosition(9, Vector3(0.5f, 0.5f, 0.f));
	request->setColor(9, Color4c(255, 255, 255, 192));
}
//------------------------------------------------------------------------------
// 球の構築
void PrimitiveDrawRequestBuilder::buildSphere(PrimitiveDrawRequest* request,
	float radius, int horizontalDivision, int verticalDivision){
	Assert(radius > 0.f);
	Assert(horizontalDivision > 2);
	Assert(verticalDivision > 2);
	int offset;
	// 頂点の構築
	int vertexCount = (verticalDivision - 1) * horizontalDivision + 2;
	request->setVertexCount(vertexCount);
	offset = 0;
	// 上
	request->setPosition(offset, Vector3(0.f, radius, 0.f));
	offset++;
	// 下
	request->setPosition(offset, Vector3(0.f, -radius, 0.f));
	offset++;
	// 中
	for(int i = 1; i < verticalDivision; i++){
		float yValue = Math::cos(Math::PI * i / verticalDivision) * radius;
		float scale = Math::sin(Math::PI * i / verticalDivision) * radius;
		for(int j = 0; j < horizontalDivision; j++){
			float radian = Math::doublePI * j / horizontalDivision;
			request->setPosition(offset, Vector3(
				Math::cos(radian) * scale, yValue, Math::sin(radian) * scale));
			offset++;
		}
	}
	Assert(offset == vertexCount);

	// インデックスの構築
	int indexCount = (verticalDivision - 2) * horizontalDivision * 4 +
		horizontalDivision * 6;
	request->setVertexIndexCount(indexCount);
	offset = 0;
	// ふた
	for(int i = 0; i < horizontalDivision; i++){
		request->setVertexIndex(offset, 0);
		offset++;
		request->setVertexIndex(offset, i + 2);
		offset++;
	}
	// 中間
	int verticalCount = verticalDivision - 1;
	for(int i = 1; i < verticalCount; i++){
		int upOffset = horizontalDivision * (i - 1) + 2;
		int downOffset = upOffset + horizontalDivision;
		for(int j = 0; j < horizontalDivision; j++){
			int leftUp = upOffset + j;
			request->setVertexIndex(offset, leftUp);
			offset++;
			int rightUp = leftUp + 1;
			if(j == (horizontalDivision - 1)){ rightUp -= horizontalDivision; }
			request->setVertexIndex(offset, rightUp);
			offset++;
			request->setVertexIndex(offset, leftUp);
			offset++;
			request->setVertexIndex(offset, downOffset + j);
			offset++;
		}
	}
	// 底
	int bottomOffset = (verticalDivision - 2) * horizontalDivision + 2;
	for(int i = 0; i < horizontalDivision; i++){
		int leftUp = i + bottomOffset;
		request->setVertexIndex(offset, leftUp);
		offset++;
		request->setVertexIndex(offset, 1);
		offset++;
		request->setVertexIndex(offset, leftUp);
		offset++;
		int rightUp = leftUp + 1;
		if(i == (horizontalDivision - 1)){ rightUp -= horizontalDivision; }
		request->setVertexIndex(offset, rightUp);
		offset++;
	}
	Assert(offset == indexCount);
}
//------------------------------------------------------------------------------
// 箱の構築
void PrimitiveDrawRequestBuilder::buildBox(
	PrimitiveDrawRequest* request, float width, float height, float depth){
	Assert(width > 0.f);
	Assert(height > 0.f);
	Assert(depth > 0.f);
	// 頂点の構築
	float halfWidth = width * 0.5f;
	float halfHeight = height * 0.5f;
	float halfDepth = depth * 0.5f;
	request->setVertexCount(8);
	request->setPosition(0, Vector3(-halfWidth, -halfHeight, -halfDepth));
	request->setPosition(1, Vector3(-halfWidth,  halfHeight, -halfDepth));
	request->setPosition(2, Vector3( halfWidth,  halfHeight, -halfDepth));
	request->setPosition(3, Vector3( halfWidth, -halfHeight, -halfDepth));
	request->setPosition(4, Vector3( halfWidth,  halfHeight,  halfDepth));
	request->setPosition(5, Vector3(-halfWidth,  halfHeight,  halfDepth));
	request->setPosition(6, Vector3(-halfWidth, -halfHeight,  halfDepth));
	request->setPosition(7, Vector3( halfWidth, -halfHeight,  halfDepth));

	// インデックスの構築
	const int indexCount = 24;
	u_short indices[indexCount] = {
		0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6,
		6, 7, 7, 4, 0, 6, 1, 5, 2, 4, 3, 7 };
	request->setVertexIndexCount(indexCount);
	for(int i = 0; i < indexCount; i++){
		request->setVertexIndex(i, indices[i]);
	}
}
//------------------------------------------------------------------------------
// 円柱の構築
void PrimitiveDrawRequestBuilder::buildCylinder(
	PrimitiveDrawRequest* request, float radius, float height, int division){
	Assert(radius > 0.f);
	Assert(height > 0.f);
	Assert(division > 2);
	// 頂点の構築
	request->setVertexCount(division * 2);
	for(int i = 0; i < division; i++){
		float radian = Math::doublePI * i / division;
		float xValue = Math::cos(radian) * radius;
		float zValue = Math::sin(radian) * radius;
		int offset = i * 2;
		request->setPosition(offset + 0, Vector3(xValue, 0.f, zValue));
		request->setPosition(offset + 1, Vector3(xValue, height, zValue));
	}

	// インデックスの構築
	request->setVertexIndexCount(division * 6);
	for(int i = 0; i < division; i++){
		int offset = i * 6;
		int leftUp = i * 2;
		int leftDown = leftUp + 1;
		request->setVertexIndex(offset + 0, leftUp);
		request->setVertexIndex(offset + 1, leftDown);
		request->setVertexIndex(offset + 2, leftUp);
		request->setVertexIndex(offset + 4, leftDown);
		if(i == (division - 1)){
			request->setVertexIndex(offset + 3, 0);
			request->setVertexIndex(offset + 5, 1);
		}else{
			request->setVertexIndex(offset + 3, leftUp + 2);
			request->setVertexIndex(offset + 5, leftDown + 2);
		}
	}
}
//------------------------------------------------------------------------------
// コーンの構築
void PrimitiveDrawRequestBuilder::buildCone(
	PrimitiveDrawRequest* request, float radius, float height, int division){
	Assert(radius > 0.f);
	Assert(height > 0.f);
	Assert(division > 2);
	// 頂点の構築
	request->setVertexCount(division + 1);
	request->setPosition(0, Vector3(0.f, height, 0.f));
	for(int i = 0; i < division; i++){
		float radian = Math::doublePI * i / division;
		float xValue = Math::cos(radian) * radius;
		float zValue = Math::sin(radian) * radius;
		request->setPosition(i + 1, Vector3(xValue, 0.f, zValue));
	}

	// インデックスの構築
	request->setVertexIndexCount(division * 4);
	for(int i = 0; i < division; i++){
		int offset = i * 4;
		request->setVertexIndex(offset + 0, 0);
		request->setVertexIndex(offset + 1, i + 1);
		request->setVertexIndex(offset + 2, i + 1);
		if(i == (division - 1)){ request->setVertexIndex(offset + 3, 1); }
		else{ request->setVertexIndex(offset + 3, i + 2); }
	}
}
//------------------------------------------------------------------------------
// ボーンの構築
void PrimitiveDrawRequestBuilder::buildBone(
	PrimitiveDrawRequest* request, CharacterModel* model){
	Assert(model != NULL);
	int boneCount = model->getBoneCount();
	Assert(boneCount != 0);
	model->buildBoneMatrix();
	// ルートボーン以外に対して一本の線が引かれる
	int vertexCount = (boneCount - 1) * 2;
	request->setVertexCount(vertexCount);
	request->enableColor(true);
	int offset = 0;
	for(int i = 0; i < boneCount; i++){
		Bone* bone = model->getBone(i);
		// ボーン位置算出行列は deform * invertPose.invert()
		Matrix34 matrix = bone->getInversePoseMatrix();
		matrix.invertTransformation();
		matrix = bone->getDeformMatrix() * matrix;
		Vector3 position = matrix * Vector3::zero;
		int childCount = bone->getBoneCount();
		for(int j = 0; j < childCount; j++){
			Bone* child = bone->getBone(j);
			// ボーン位置算出行列は deform * invertPose.invert()
			Matrix34 childMatrix = child->getInversePoseMatrix();
			childMatrix.invertTransformation();
			childMatrix = child->getDeformMatrix() * childMatrix;
			Vector3 childPosition = childMatrix * Vector3::zero;
			request->setPosition(offset, position);
			request->setColor(offset, Color4c::white);
			offset++;
			request->setPosition(offset, childPosition);
			request->setColor(offset, Color4c(255, 255, 255, 64));
			offset++;
		}
	}
	Assert(vertexCount == offset)
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
