//------------------------------------------------------------------------------
// 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/Renderer/Renderer.h"
#include "Graphics/Renderer/DrawRequest.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Graphics/System/GraphicsDeviceCapacity.h"
#include "Graphics/Camera/Camera.h"
#include "Graphics/Light/LightManager.h"
#include "Graphics/Mesh/Mesh.h"
#include "Graphics/Material/Material.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 生成、破棄
//------------------------------------------------------------------------------
// コンストラクタ
Renderer::Renderer() : meshList_(initialMeshListSize){
	// デバイスオブジェクトホルダ登録
	LampGraphics::addDeviceObjectHolder(this);
	buildRendererStateBlock();
	drawRequest_ = new DrawRequest();
}
//------------------------------------------------------------------------------
// デストラクタ
Renderer::~Renderer(){
	SafeDelete(drawRequest_);
	SafeRelease(rendererStateBlock_);
	// デバイスオブジェクトホルダ登録解除
	LampGraphics::removeDeviceObjectHolder(this);
}
//------------------------------------------------------------------------------
// レンダリング
//------------------------------------------------------------------------------
// レンダリング準備を行う
void Renderer::renderingSetup(Scene* scene){
	RenderingDevice* device = RenderingDevice::getInstance();
	scene_ = scene;
	// メッシュリストの構築
	buildMeshList();
	// 描画リクエストの初期化
	initializeDrawRequest();
}
//------------------------------------------------------------------------------
// レンダリングを行う
void Renderer::rendering(){
	RenderingDevice* device = RenderingDevice::getInstance();
	// デフォルトステートブロック適用
	device->applyDefaultStateBlock();
	// レンダラステートブロック適用
	device->applyStateBlock(rendererStateBlock_);
	// グローバル設定の初期化
	initializeGlobalSettings();
	// 描画
	if(device->beginScene()){
		int meshCount = meshList_.getCount();
		for(int i = 0; i < meshCount; i++){
			Mesh* mesh = meshList_.get(i);
			Material* material = mesh->getMaterial();
			drawRequest_->setMesh(mesh);
			// ブレンディングを有効にする
			if(drawRequest_->isBlendEnabled()){
				device->setBlending(true);
				device->setRenderState(
					D3DRS_ALPHAREF, blendingAlphaTestBorder_);
			}
			// ローカルライトリストの構築
			if(material->useLight()){
				drawRequest_->clearLocalLights();
				scene_->getLocalLightList(mesh, drawRequest_);
			}
			// 描画
			material->draw(drawRequest_);
		}
		// 描画後始末
		device->endScene();
	}
	// デフォルトステートブロック適用
	device->applyDefaultStateBlock();
}
//------------------------------------------------------------------------------
// メッシュリストの構築
void Renderer::buildMeshList(){
	meshList_.clear();
	Camera* camera = scene_->getCurrentCamera();
	scene_->getMeshList(&meshList_, camera);
	int meshCount = meshList_.getCount();
	// カメラとの距離の二乗をテンポラリデータに設定
	Vector3 cameraPosition = camera->getPosition();
	for(int i = 0; i < meshCount; i++){
		Mesh* mesh = meshList_.get(i);
		mesh->setRenderingTemporaryData(
			(cameraPosition - mesh->getWorldCenter()).getSquaredLength());
	}
	// メッシュリストのソート
	meshList_.sort(sortMeshList);
/* 正しいソートが行われているかprintfチェック
	for(int i = 0; i < meshCount; i++){
		Mesh* mesh = meshList_.get(i);
		Material* material = mesh->getMaterial();
		DebugOut("%3d %d %2d 0x%08x %6.1f %s\n",
			i, material->getBlendMode(), material->getPriority(),
			material,
			mesh->getRenderingTemporaryData(),
			material->getName().getBytes());
	}
	DebugOut("\n");// ここにブレークポイントを仕掛ける
//*/
}
//------------------------------------------------------------------------------
// メッシュリストのソート
int Renderer::sortMeshList(Mesh* const* left, Mesh* const* right){
	const Mesh* leftMesh = (*left);
	const Mesh* rightMesh = (*right);
	const Material* leftMaterial = leftMesh->getMaterial();
	const Material* rightMaterial = rightMesh->getMaterial();
	if(!leftMaterial->isBlendEnabled()){
		// アルファソート
		if(rightMaterial->isBlendEnabled()){ return -1; }
		// アルファでないメッシュのソート
		// マテリアル優先度で比較
		int priority =
			rightMaterial->getPriority() - leftMaterial->getPriority();
		if(priority != 0){ return priority; }
		// マテリアルのアドレスでソート
		int leftAddress = (int)((*(u_int*)&leftMaterial) & 0x8fffffff);
		int rightAddress = (int)((*(u_int*)&rightMaterial) & 0x8fffffff);
		int address = leftAddress - rightAddress;
		if(address != 0){ return address; }
		// 手前から奥にZソート
		float cameraSquaredDistance = rightMesh->getRenderingTemporaryData() -
			leftMesh->getRenderingTemporaryData();
		if(cameraSquaredDistance > 0.f){ return -1; }
		else{ return 1; }
	}else{
		// アルファソート
		if(!rightMaterial->isBlendEnabled()){ return 1; }
		// アルファメッシュのソート
		// 奥から手前にZソート
		float cameraSquaredDistance = leftMesh->getRenderingTemporaryData() -
			rightMesh->getRenderingTemporaryData();
		if(cameraSquaredDistance > 0.f){ return -1; }
		else{ return 1; }
	}
	return 0;
}
//------------------------------------------------------------------------------
// 描画リクエストの初期化
void Renderer::initializeDrawRequest(){
	drawRequest_->clear();
	drawRequest_->setFog(scene_->getFog());
	drawRequest_->setCamera(scene_->getCurrentCamera());
	// グローバルライトの初期化
	LightManager* lightManager = scene_->getLightManager();
	int lightCount = lightManager->getCount();
	for(int i = 0; i < lightCount; i++){
		Light* light = lightManager->get(i);
		// 無効なライトを無視する
		if(!light->isGlobalEnabled()){ continue; }
		if(light->isAmbientLight()){
			drawRequest_->addAmbientLight(light->castAmbientLight());
		}else if(light->isDirectionalLight()){
			drawRequest_->addDirectionalLight(light->castDirectionalLight());
		}
	}
}
//------------------------------------------------------------------------------
// グローバル設定の初期化
void Renderer::initializeGlobalSettings(){
	// 一回のレンダリング中には変化しない設定を行う
	RenderingDevice* device = RenderingDevice::getInstance();
	// カメラ行列の設定
	Camera* camera = drawRequest_->getCamera();
	device->setProjectionMatrix(camera->getProjectionMatrix());
	device->setViewMatrix(camera->getViewMatrix());
	// フォグの設定
	device->setFog(drawRequest_->getFog());
}
//------------------------------------------------------------------------------
// レンダラステートブロックの構築
void Renderer::buildRendererStateBlock(){
	RenderingDevice* device = RenderingDevice::getInstance();
	GraphicsDeviceCapacity* capacity = GraphicsDeviceCapacity::getInstance();
	// デフォルトステートブロック適用
	device->applyDefaultStateBlock();
	// レンダラステートブロックの構築開始
	device->beginStateBlock();

	// カリングを逆順にする
	device->setRenderState(D3DRS_CULLMODE, D3DCULL_CW);

	// テクスチャサンプラにミップマップと線形補間を設定する
	int textureStageCount = capacity->getMaxTextureBlendStages();
	for(int i = 0; i < textureStageCount; i++){
		device->setSamplerState(i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		device->setSamplerState(i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		device->setSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	}

	// 頂点カラーをエミッシブとして使用する
	device->setRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
	device->setRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);
	device->setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);

	// アルファテストの設定
	// アルファブレンディングを行わなくてもアルファテストは行う
	device->setRenderState(D3DRS_ALPHATESTENABLE, true);
	device->setRenderState(D3DRS_ALPHAREF, alphaTestBorder_);
	device->setRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

	// レンダラステートブロックの構築完了
	rendererStateBlock_ = device->endStateBlock();
	// デフォルトステートブロック適用
	device->applyDefaultStateBlock();
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
