//------------------------------------------------------------------------------
// 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/DrawRequest.h"
#include "Graphics/Renderer/Renderer.h"
#include "Graphics/SceneNode/SceneNode.h"
#include "Graphics/Light/AmbientLight.h"
#include "Graphics/Light/DirectionalLight.h"
#include "Graphics/Light/PointLight.h"
#include "Graphics/Model/Model.h"
#include "Graphics/Mesh/Mesh.h"
#include "Graphics/Material/Material.h"

namespace Lamp{

// インスタンス
DrawRequest* DrawRequest::instance_ = NULL;

//------------------------------------------------------------------------------
// コンストラクタ
DrawRequest::DrawRequest(){
	clear();
	Assert(instance_ == NULL);
	instance_ = this;
}
//------------------------------------------------------------------------------
// デストラクタ
DrawRequest::~DrawRequest(){
	Assert(instance_ != NULL);
	instance_ = NULL;
}
//------------------------------------------------------------------------------
// クリア
void DrawRequest::clear(){
	directionalLights_.clear();
	ambientLights_.clear();
	localLights_.clear();
	fog_ = NULL;
	camera_ = NULL;
	mesh_ = preMesh_ = NULL;
	sceneNode_ = preSceneNode_ = NULL;
	model_ = preModel_ = NULL;
	meshData_ = preMeshData_ = NULL;
	material_ = preMaterial_ = NULL;
}
//------------------------------------------------------------------------------
// メッシュの設定
void DrawRequest::setMesh(Mesh* mesh){
	Assert(mesh != NULL);
	preMesh_ = mesh_;
	preSceneNode_ = sceneNode_;
	preModel_ = model_;
	preMeshData_ = meshData_;
	preMaterial_ = material_;
	mesh_ = mesh;
	model_ = mesh_->getParent();
	sceneNode_ = model_->getParent();
	meshData_ = mesh_->getMeshData();
	material_ = mesh_->getMaterial();
	Assert(sceneNode_ != NULL);
	Assert(model_ != NULL);
	Assert(meshData_ != NULL);
	Assert(material_ != NULL);
}
//------------------------------------------------------------------------------
// 正規化を必要とするか
bool DrawRequest::requireNormalize() const{
	// シーンノードにスケールが含まれるなら正規化を行う
	bool result = sceneNode_->isGlobalScaled();
	// メッシュが正規化を必要とするなら正規化を行う
	result |= mesh_->requireNormalize();
	return result;
}
//------------------------------------------------------------------------------
// パイプラインモードに変更があったか
bool DrawRequest::isPipelineModeChanged() const{
	if(preMaterial_ == NULL){ return true; }
	return (material_->getPipelineMode() !=
		preMaterial_->getPipelineMode());
}
//------------------------------------------------------------------------------
// ブレンドが有効になった
bool DrawRequest::isBlendEnabled() const{
	Assert(material_ != NULL);
	bool alphaBlend = material_->isBlendEnabled();
	if(preMaterial_ == NULL){
		return alphaBlend;
	}else{
		bool preAlphaBlend = preMaterial_->isBlendEnabled();
		// アルファ状態からアルファでない状態にはならない
		Assert(!(preAlphaBlend && (!alphaBlend)));
		if((!preAlphaBlend) && alphaBlend){ return true; }
	}
	return false;
}
//------------------------------------------------------------------------------
// アンビエントライトの追加
void DrawRequest::addAmbientLight(AmbientLight* ambientLight){
	Assert(ambientLight->isAmbientLight());
	ambientLights_.add(ambientLight);
}
//------------------------------------------------------------------------------
// アンビエント色の取得
Color3f DrawRequest::getAmbientColor() const{
	Assert(material_ != NULL);
	u_int lightMask = material_->getLightMask();
	Color3f ambientColor(Color3f::black);
	int ambientLightCount = ambientLights_.getCount();
	for(int i = 0; i < ambientLightCount; i++){
		AmbientLight* ambientLight = ambientLights_.get(i);
		// ライトマスク判定
		if((ambientLight->getLightMask() & lightMask) == 0){ continue; }
		ambientColor += ambientLight->getColor();
	}
	return ambientColor;
}
//------------------------------------------------------------------------------
// ディレクショナルライトの追加
void DrawRequest::addDirectionalLight(DirectionalLight* directionalLight){
	Assert(directionalLight->isDirectionalLight());
	directionalLights_.add(directionalLight);
}
//------------------------------------------------------------------------------
// ローカルライトの追加
void DrawRequest::addLocalLight(LocalLight* localLight){
	Assert(localLight->isLocalLight());
	localLights_.add(localLight);
}
//------------------------------------------------------------------------------
// ローカルライトのソート用コールバック
int DrawRequest::sortLocalLightsCallback(
	LocalLight* const* left, LocalLight* const* right){
	return instance_->sortLocalLightsImprement((*left), (*right));
}
//------------------------------------------------------------------------------
// ローカルライトのソート実装
int DrawRequest::sortLocalLightsImprement(LocalLight* left, LocalLight* right){
	Assert(mesh_ != NULL);
	// メッシュに近いものから遠いものへソート
	// メッシュ中心に対して最も明るいものでソートしたほうがいい
	// 輝度と減衰(distance - near)/(far - near)をかけたもので
	// LocalLightにgetWorldLuminance()やgetWorldSquaredDistance()
	const Vector3& meshCenter = mesh_->getWorldCenter();
	PointLight* pointLeft = left->castPointLight();
	Assert(pointLeft != NULL);
	PointLight* pointRight = right->castPointLight();
	Assert(pointRight != NULL);
	float leftDistance =
		(pointLeft->getWorldPosition() - meshCenter).getSquaredLength();
	float rightDistance =
		(pointRight->getWorldPosition() - meshCenter).getSquaredLength();
	if((rightDistance - leftDistance) > 0.f){ return -1; }
	else{ return 1; }
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
