//------------------------------------------------------------------------------
// 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/Shader/FixedShader.h"
#include "Graphics/Renderer/RenderingDevice.h"
#include "Graphics/Renderer/DrawRequest.h"
#include "Graphics/Fog/Fog.h"
#include "Graphics/SceneNode/SceneNode.h"
#include "Graphics/Light/DirectionalLight.h"
#include "Graphics/Light/PointLight.h"
#include "Graphics/MeshData/MeshData.h"
#include "Graphics/Material/Material.h"
#include "Graphics/Texture/SurfaceTexture.h"


#include "Graphics/Model/Model.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
FixedShader::FixedShader(){
}
//------------------------------------------------------------------------------
// デストラクタ
FixedShader::~FixedShader(){
}
//------------------------------------------------------------------------------
// ステートブロック
//------------------------------------------------------------------------------
// マテリアル開始の構築
void FixedShader::buildMaterialStart(Material* material){
	Shader::buildMaterialStart(material);
}
//------------------------------------------------------------------------------
// マテリアル終了の構築
//void FixedShader::buildMaterialEnd(Material* material){
//	Shader::buildMaterialEnd(material);
//}
//------------------------------------------------------------------------------
// テクスチャ
//------------------------------------------------------------------------------
// 固定機能テクスチャ設定
void FixedShader::setFixedTexture(int textureStage, Texture* texture){
	// テクスチャ設定
	device_->setTexture(textureStage, texture);
	// サーフェーステクスチャ設定
	SurfaceTexture* surfaceTexture = texture->castSurfaceTexture();
	if(surfaceTexture != NULL){
		// UVのリピートとオフセット
		device_->setTextureTransform2(textureStage,
			surfaceTexture->getRepeatUV(), surfaceTexture->getOffsetUV());
		// アドレスモード設定
		device_->setTextureAddressMode2(textureStage,
			surfaceTexture->getAddressModeU(),
			surfaceTexture->getAddressModeV());
	}
}
//------------------------------------------------------------------------------
// 固定機能ベーステクスチャの設定
int FixedShader::setFixedBaseTexture(
	int colorStage, Texture* baseTexture, int baseUVIndex){
	if(baseTexture == NULL){ return colorStage; }
	// テクスチャ設定
	setFixedTexture(colorStage, baseTexture);
	// Current * baseTexture
	device_->setColorTextureStage(colorStage,
		D3DTOP_MODULATE, D3DTA_CURRENT, D3DTA_TEXTURE, baseUVIndex);
	colorStage++;
	return colorStage;
}
//------------------------------------------------------------------------------
// 固定機能ライトテクスチャの設定
int FixedShader::setFixedLightTexture(
	int colorStage, Texture* lightTexture, int lightUVIndex){
	if(lightTexture == NULL){ return colorStage; }
	// テクスチャ設定
	setFixedTexture(colorStage, lightTexture);
	// Current + lightTexture
	device_->setColorTextureStage(colorStage,
		D3DTOP_ADD, D3DTA_CURRENT, D3DTA_TEXTURE, lightUVIndex);
	colorStage++;
	return colorStage;
}
//------------------------------------------------------------------------------
// 固定機能汚れテクスチャの設定
int FixedShader::setFixedStainTexture(
	int colorStage, Texture* stainTexture, int stainUVIndex){
	if(stainTexture == NULL){ return colorStage; }
	// テクスチャ設定
	setFixedTexture(colorStage, stainTexture);
	// Current * stainTexture
	device_->setColorTextureStage(colorStage,
		D3DTOP_MODULATE, D3DTA_CURRENT, D3DTA_TEXTURE, stainUVIndex);
	colorStage++;
	return colorStage;
}
//------------------------------------------------------------------------------
// 描画の初期化
//------------------------------------------------------------------------------
// 固定機能描画のセットアップ
void FixedShader::setupFixedDraw(DrawRequest* request){
	// 法線の正規化設定
	device_->setRenderState(D3DRS_NORMALIZENORMALS,
		request->requireNormalize());
	// 行列の設定
	setMatrixFixed(request);
	// フォグオプションの設定
	setFogOptionFixed(request);
	// ライトのセットアップ
	setupFixedLight(request);
}
//------------------------------------------------------------------------------
// 固定機能行列の設定
void FixedShader::setMatrixFixed(DrawRequest* request){
	if(request->isSceneNodeChanged()){
		device_->setWorldMatrix(request->getSceneNode()->getWorldMatrix());
	}
}
//------------------------------------------------------------------------------
// 固定機能フォグオプションの設定
void FixedShader::setFogOptionFixed(DrawRequest* request){
	Material::FogOption fogOption = request->getMaterial()->getFogOption();
	if(fogOption == Material::fogOptionDisable){
		device_->setRenderState(D3DRS_FOGENABLE, false);
	}else if(fogOption == Material::fogOptionBlack){
		device_->setRenderState(D3DRS_FOGCOLOR, 0);
	}
}
//------------------------------------------------------------------------------
// 固定機能ライトのセットアップ
void FixedShader::setupFixedLight(DrawRequest* request){
	Material* material = request->getMaterial();
	// ライトを使用しないのならば何もしない
	if(!material->useLight()){ return; }

	// アンビエントライトの設定
	// Requestの中でlightMask判定が行われている
	device_->setAmbientColor(request->getAmbientColor());

	// ディレクショナルライトの設定
	u_int lightMask = material->getLightMask();
	int lightCount = 0;
	int directionalLightCount = request->getDirectionalLightCount();
	for(int i = 0; i < directionalLightCount; i++){
		DirectionalLight* directionalLight = request->getDirectionalLight(i);
		// lightMask判定
		if((directionalLight->getLightMask() & lightMask) == 0){ continue; }
		device_->enableDirectionalLight(lightCount, directionalLight);
		lightCount++;
		// 最大ライト数分設定したら終了する
		if(lightCount == maxActiveLightCount_){ return; }
	}

	// ローカルライトの設定
	// シーンからローカルライトリストを取得する時にlightMask判定が行われている
	int localLightCount = request->getLocalLightCount();
	// ローカルライトが最大数を超えている場合ソートする
	if(localLightCount + lightCount > maxActiveLightCount_){
		request->sortLocalLights();
	}
	for(int i = 0; i < localLightCount; i++){
		LocalLight* localLight = request->getLocalLight(i);
		if(localLight->isPointLight()){
			device_->enablePointLight(lightCount, localLight->castPointLight());
		}else{
			ErrorOut("FixedShader::setupFixedLight() 非サポートのライトです");
		}
		lightCount++;
		// 最大ライト数分設定したら終了する
		if(lightCount == maxActiveLightCount_){ return; }
	}

	// ライトを閉じる
	device_->closeLight(lightCount);
}
//------------------------------------------------------------------------------
// 描画
//------------------------------------------------------------------------------
// 固定機能描画
void FixedShader::drawFixed(DrawRequest* request){
	Mesh* mesh = request->getMesh();
	// キャラクタ変形
	bool deformed = false;
	deformed |= mesh->characterDeform();
	if(deformed){
		// メッシュは必ず一回しか描画されないのでストリームキャッシュを行えない
		// 変形頂点記述を設定
		device_->setVertexDeclaration(mesh->getDeformedVertexDeclaration());
		// 変形頂点バッファを設定
		device_->setVertexBuffer(mesh->getDeformedVertexBuffer(),
			mesh->getDeformedVertexSize());
	// 同じメッシュデータでパイプラインに変更が無ければストリーム設定を行わない
	}else if(request->isMeshDataChanged() || request->isPipelineModeChanged()){
		// 頂点記述を設定
		device_->setVertexDeclaration(mesh->getVertexDeclaration());
		// 頂点バッファを設定
		device_->setVertexBuffer(mesh->getVertexBuffer(),
			mesh->getVertexSize());
	}
	// 描画コール
	drawCall(request);
}
//------------------------------------------------------------------------------
// 描画の後始末
//------------------------------------------------------------------------------
// 固定機能描画のリセット
void FixedShader::resetFixedDraw(DrawRequest* request){
	// フォグオプションのリセット
	resetFogOptionFixed(request);
}
//------------------------------------------------------------------------------
// 固定機能フォグオプションのリセット
void FixedShader::resetFogOptionFixed(DrawRequest* request){
	Material::FogOption fogOption = request->getMaterial()->getFogOption();
	if(fogOption == Material::fogOptionDisable){
		device_->setRenderState(D3DRS_FOGENABLE, true);
	}else if(fogOption == Material::fogOptionBlack){
		device_->setRenderState(D3DRS_FOGCOLOR,
			request->getFog()->getColor().getARGB());
	}
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
