//------------------------------------------------------------------------------
// 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 "Graphics2D/Renderer/SpriteRenderer.h"
#include "Graphics2D/Renderer/SpriteGraphicsBuffer.h"
#include "Graphics2D/Renderer/SpriteRenderState.h"
#include "Graphics2D/Renderer/SpriteRequest.h"
#include "Graphics/Renderer/RenderingDevice.h"

#include "Graphics2D/SpriteState/ColorSpriteState.h"
#include "Graphics2D/SpriteState/AddressModeSpriteState.h"
#include "Graphics2D/SpriteState/BlendSpriteState.h"
#include "Graphics2D/SpriteState/ViewportSpriteState.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 生成、破棄
//------------------------------------------------------------------------------
// コンストラクタ
SpriteRenderer::SpriteRenderer(int maxPriority, int defaultPriority) :
	maxPriority_(maxPriority){
	Assert(maxPriority_ > 0);
	setDefaultPriority(defaultPriority);
	requests_ = new ArrayList<SpriteRequest*>[maxPriority_];
	graphicsBuffer_ = new SpriteGraphicsBuffer();
}
//------------------------------------------------------------------------------
// デストラクタ
SpriteRenderer::~SpriteRenderer(){
	SafeDelete(graphicsBuffer_);
	SafeArrayDelete(requests_);
}
//------------------------------------------------------------------------------
// リクエスト
//------------------------------------------------------------------------------
// リクエスト
void SpriteRenderer::request(SpriteRequest* spriteRequest, int priority){
	Assert((priority >= 0) && (priority < maxPriority_));
	requests_[priority].add(spriteRequest);
}
//------------------------------------------------------------------------------
// デフォルトステートのリクエスト
void SpriteRenderer::requestDefaultState(int priority){
	request(&ColorSpriteState::defaultState, priority);
	request(&AddressModeSpriteState::defaultState, priority);
	request(&BlendSpriteState::defaultState, priority);
	request(&ViewportSpriteState::defaultState, priority);
}
//------------------------------------------------------------------------------
// デフォルトステートの適用
void SpriteRenderer::applyDefaultState(SpriteRenderState* renderState){
	((ColorSpriteState*)(&ColorSpriteState::defaultState))->
		render(renderState);
	((AddressModeSpriteState*)(&AddressModeSpriteState::defaultState))->
		render(renderState);
	((BlendSpriteState*)(&BlendSpriteState::defaultState))->
		render(renderState);
	((ViewportSpriteState*)(&ViewportSpriteState::defaultState))->
		render(renderState);
}
//------------------------------------------------------------------------------
// レンダリング
//------------------------------------------------------------------------------
// レンダリング
void SpriteRenderer::render(int startPriority, int endPriority){
	Assert(startPriority <= endPriority);
	Assert((startPriority >= 0) && (endPriority < maxPriority_));
	RenderingDevice* device = RenderingDevice::getInstance();
	SpriteRenderState renderState(graphicsBuffer_);

	// デフォルトステートブロックの適用
	device->applyDefaultStateBlock();

	// グラフィックスバッファのセットアップ
	graphicsBuffer_->setup();

	// レンダラステート適用
	applyRendererState();

	// スプライトデフォルトステートの適用
	applyDefaultState(&renderState);

	// 描画開始
	if(device->beginScene()){
		// endPriorityを含むループ
		for(int i = startPriority; i <= endPriority; i++){
			// レンダリング
			int count = requests_[i].getCount();
			for(int j = 0; j < count; j++){
				SpriteRequest* request = requests_[i].get(j);
				request->render(&renderState);
			}
			// リクエストをクリアする
			requests_[i].clear();
		}
		// 描画後始末
		graphicsBuffer_->render();
		device->endScene();
	}

	// デフォルトステートブロックの適用
	device->applyDefaultStateBlock();
}
//------------------------------------------------------------------------------
// レンダラステートの適用
void SpriteRenderer::applyRendererState(){
	// レンダリング中に不変のステートを設定する
	RenderingDevice* device = RenderingDevice::getInstance();

	//--------------------------------------------------------------------------
	// 静的設定
	//--------------------------------------------------------------------------
	// ライティング無し
	device->setRenderState(D3DRS_LIGHTING, false);
	// カリング無し
	device->setRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	// Z書き込み無し
	device->setRenderState(D3DRS_ZWRITEENABLE, false);
	// Zテスト無し
	device->setZTest(false);

	// カラーステージの設定
	device->setTextureState(0, D3DTSS_COLOROP, D3DTOP_MODULATE4X);
	device->setTextureState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
	device->setTextureState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
	// アルファステージの設定
	device->setTextureState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
	device->setTextureState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR);
	device->setTextureState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

	// テクスチャサンプラにミップマップと線形補間を設定する
	device->setSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	device->setSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	device->setSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

	// アルファテストの設定
	device->setRenderState(D3DRS_ALPHATESTENABLE, true);
	device->setRenderState(D3DRS_ALPHAREF, 1);
	device->setRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

	//--------------------------------------------------------------------------
	// 動的設定
	//--------------------------------------------------------------------------
	// レンダーターゲットサイズを取得する
	DimensionF renderTargetSize;
	renderTargetSize.set(device->getRenderTargetSize());

	// 射影行列
	Matrix44 projectionMatrix;
	float nearZ = 1.f;
	float farZ = 10001.f;
	projectionMatrix.set(
		2.f / renderTargetSize.width, 0.f, 0.f, 0.f,
		0.f, 2.f / renderTargetSize.height, 0.f, 0.f,
		0.f, 0.f, 1.f / (nearZ - farZ), nearZ / (nearZ - farZ),
		0.f, 0.f, 0.f, 1.f);
	device->setProjectionMatrix(projectionMatrix);

	// ビュー行列
	Matrix44 viewMatrix;
	Vector3 viewPosition(renderTargetSize.width * 0.5f,
		renderTargetSize.height * 0.5f, (farZ - nearZ) * 0.5f + nearZ);
	viewMatrix.setTranslation(-viewPosition);
	viewMatrix.addScale(Vector3(1.f, -1.f, 1.f));
	device->setViewMatrix(viewMatrix);

}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
