//------------------------------------------------------------------------------
// 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/PrimitiveRenderer.h"
#include "Graphics/PrimitiveRenderer/PrimitiveDrawRequestBuilder.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Graphics/Camera/Camera.h"
#include "Graphics/Material/Material.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 生成、破棄
//------------------------------------------------------------------------------
// コンストラクタ
PrimitiveRenderer::PrimitiveRenderer() :
	positionDeclaration_(NULL), positionColorDeclaration_(NULL){
	LampGraphics::addDeviceObjectHolder(this);
	// 各プリミティブの構築
	PrimitiveDrawRequestBuilder::buildPoint(&point_);	
	PrimitiveDrawRequestBuilder::buildAxisPoint(&axisAxisPoint_);	
	PrimitiveDrawRequestBuilder::buildAxis(&axis_);	
	PrimitiveDrawRequestBuilder::buildArrow(&arrow_);	
	PrimitiveDrawRequestBuilder::buildGrid(&grid_, 10, 10);	
	PrimitiveDrawRequestBuilder::buildPlane(&plane_);	
	PrimitiveDrawRequestBuilder::buildSphere(&sphere_, 1.f, 12, 8);	
	PrimitiveDrawRequestBuilder::buildBox(&box_, 1.f, 1.f, 1.f);	
	PrimitiveDrawRequestBuilder::buildCylinder(&cylinder_, 1.f, 1.f, 12);	
	PrimitiveDrawRequestBuilder::buildCone(&cone_, 1.f, 1.f, 12);	
}
//------------------------------------------------------------------------------
// デストラクタ
PrimitiveRenderer::~PrimitiveRenderer(){
	LampGraphics::removeDeviceObjectHolder(this);
	// グラフィックスオブジェクトを解放する
	invalidateGraphicsDeviceObjects();
}
//------------------------------------------------------------------------------
// レンダリング
//------------------------------------------------------------------------------
// レンダリング
void PrimitiveRenderer::render(const Matrix44& viewMatrix,
	const Matrix44& projectionMatrix){
	if(requests_.getCount() == 0){ return; }
	//--------------------------------------------------------------------------
	// 初期化
	RenderingDevice* device = RenderingDevice::getInstance();
	// デフォルトステートブロック適用
	device->applyDefaultStateBlock();
	// ビュー行列の設定
	device->setViewMatrix(viewMatrix);
	// 投影行列の設定
	device->setProjectionMatrix(projectionMatrix);
	// カリングを無効にする
	device->setRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	// ライティングを無効にする
	device->setRenderState(D3DRS_LIGHTING, false);
	// デフォルトのマテリアルを設定する、頂点カラーが無い場合ディフューズの白になる
	device->setMaterial(Color3f::white, Color3f::black, Color3f::black,
		Color3f::black, 0.f, 1.f);
	// カラーステージの設定（Diffuse * TextureFactor）
	device->setTextureState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
	device->setTextureState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
	device->setTextureState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
	// アルファステージの設定（Diffuse * TextureFactor）
	device->setTextureState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
	device->setTextureState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
	device->setTextureState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
	// ブレンディングを有効にする
	device->setBlending(true);
	device->setBlendMode(Material::blendModeAdd,
		Material::blendStateSourceAlpha,
		Material::blendStateInverseSourceAlpha);

	//--------------------------------------------------------------------------
	// 描画
	if(device->beginScene()){
		Request request;
		int requestCount = requests_.getCount();
		for(int i = 0; i < requestCount; i++){
			renderRequest(requests_[i]);
			// リクエストをクリアしておく
			requests_.set(i, request);
		}
		device->endScene();
	}

	//--------------------------------------------------------------------------
	// 後始末
	// デフォルトステートブロック適用
	device->applyDefaultStateBlock();
	// リクエストのクリア
	requests_.clear();
}
//------------------------------------------------------------------------------
// レンダリング
void PrimitiveRenderer::render(Camera* camera){
	render(camera->getViewMatrix(), camera->getProjectionMatrix());
}
//------------------------------------------------------------------------------
// リクエストの描画
void PrimitiveRenderer::renderRequest(Request& request){
	RenderingDevice* device = RenderingDevice::getInstance();
	// 行列の設定
	device->setWorldMatrix(request.matrix_);
	// 色をテクスチャ定数に設定
	device->setRenderState(D3DRS_TEXTUREFACTOR, request.color_.getARGB());
	// Zテストの設定
	device->setZTest(request.zTest_);

	// プリミティブの設定
	PrimitiveDrawRequest& primitive = request.primitive_;
	// 頂点定義の設定
	int vertexSize = primitive.getVertexSize();
	if(!primitive.hasColor()){
		setPositionVertexDeclaration();
	}else{
		setPositionColorVertexDeclaration();
	}
	// 頂点バッファの設定
	device->setVertexBuffer(primitive.getVertexBuffer(), vertexSize);

	// 描画
	bool hasIndices = primitive.hasVertexIndices();
	int vertexCount = primitive.getVertexCount();
	if(hasIndices){
		device->setIndexBuffer(primitive.getIndexBuffer());
		int indexCount = primitive.getVertexIndexCount();
		Assert((indexCount % 2) == 0);
		device->drawIndexedLineList(vertexCount, indexCount / 2);
	}else{
		Assert((vertexCount % 2) == 0);
		device->drawLineList(vertexCount / 2);
	}
}
//------------------------------------------------------------------------------
// リクエスト
//------------------------------------------------------------------------------
// リクエスト
void PrimitiveRenderer::request(const PrimitiveDrawRequest& primitive,
	const Matrix34& matrix, Color4c color, bool zTest){
	Request request;
	request.primitive_ = primitive;
	request.matrix_ = matrix;
	request.color_ = color;
	request.zTest_ = zTest;
	requests_.add(request);
}
//------------------------------------------------------------------------------
// 線のリクエスト
void PrimitiveRenderer::requestLine(int vertexCount, Vector3* positions,
	const Matrix34& matrix, Color4c color, bool zTest){
	Assert((vertexCount > 1) && ((vertexCount % 2) == 0));
	Assert(positions != NULL);
	PrimitiveDrawRequest primitive;
	primitive.setVertexCount(vertexCount);
	for(int i = 0; i < vertexCount; i++){
		primitive.setPosition(i, positions[i]);
	}
	request(primitive, matrix, color, zTest);
}
//------------------------------------------------------------------------------
// 線のリクエスト
void PrimitiveRenderer::requestLine(int vertexCount, Vector3* positions,
	Color4c* colors, const Matrix34& matrix, Color4c color, bool zTest){
	Assert((vertexCount > 1) && ((vertexCount % 2) == 0));
	Assert(positions != NULL);
	Assert(colors != NULL);
	PrimitiveDrawRequest primitive;
	primitive.setVertexCount(vertexCount);
	primitive.enableColor(true);
	for(int i = 0; i < vertexCount; i++){
		primitive.setPosition(i, positions[i]);
		primitive.setColor(i, colors[i]);
	}
	request(primitive, matrix, color, zTest);
}
//------------------------------------------------------------------------------
// デバイスオブジェクト
//------------------------------------------------------------------------------
// デバイスオブジェクトの無効化
void PrimitiveRenderer::invalidateGraphicsDeviceObjects(){
	// グラフィックスオブジェクトを解放する
	SafeRelease(positionDeclaration_);
	SafeRelease(positionColorDeclaration_);
}
//------------------------------------------------------------------------------
// 頂点記述
//------------------------------------------------------------------------------
// 位置の頂点記述を設定
void PrimitiveRenderer::setPositionVertexDeclaration(){
	RenderingDevice* device = RenderingDevice::getInstance();
	if(positionDeclaration_ == NULL){
		device->createVertexDeclaration(&positionDeclaration_,
			true, 0, 0, false, false, 0, NULL);
	}
	device->setVertexDeclaration(positionDeclaration_);
}
//------------------------------------------------------------------------------
// 位置とカラーの頂点記述を設定
void PrimitiveRenderer::setPositionColorVertexDeclaration(){
	RenderingDevice* device = RenderingDevice::getInstance();
	if(positionColorDeclaration_ == NULL){
		device->createVertexDeclaration(&positionColorDeclaration_,
			true, 0, 0, false, true, 0, NULL);
	}
	device->setVertexDeclaration(positionColorDeclaration_);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
