//------------------------------------------------------------------------------
// 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/SpriteGraphicsBuffer.h"
#include "Graphics/System/LampGraphics.h"
#include "Graphics/Renderer/RenderingDevice.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 生成、破棄
//------------------------------------------------------------------------------
// コンストラクタ
SpriteGraphicsBuffer::SpriteGraphicsBuffer() :
	indexBuffer_(NULL), vertexBuffer_(NULL), vertexDeclaration_(NULL),
	topIndex_(0), bottomIndex_(0){
	LampGraphics::addDeviceObjectHolder(this);
}
//------------------------------------------------------------------------------
// デストラクタ
SpriteGraphicsBuffer::~SpriteGraphicsBuffer(){
	LampGraphics::removeDeviceObjectHolder(this);
	SafeRelease(vertexDeclaration_);
	SafeRelease(vertexBuffer_);
	SafeRelease(indexBuffer_);
}
//------------------------------------------------------------------------------
// 描画
//------------------------------------------------------------------------------
// リクエスト
void SpriteGraphicsBuffer::request(
	const Point2f& minPosition, const Point2f& maxPosition,
	const TexCoord2& minUV, const TexCoord2& maxUV){
	Assert((vertexBuffer_ != NULL) && (indexBuffer_ != NULL) &&
		(vertexDeclaration_ != NULL));
	Assert(topIndex_ <= bottomIndex_);

	// データの書き込み
	SpriteData* data = &dataBuffer_[bottomIndex_];
	data->position0.set(minPosition.x, minPosition.y, 0.f);
	data->uv0.set(minUV.u, minUV.v);
	data->position1.set(minPosition.x, maxPosition.y, 0.f);
	data->uv1.set(minUV.u, maxUV.v);
	data->position2.set(maxPosition.x, minPosition.y, 0.f);
	data->uv2.set(maxUV.u, minUV.v);
	data->position3.set(maxPosition.x, maxPosition.y, 0.f);
	data->uv3.set(maxUV.u, maxUV.v);

	// バッファが満たされていればレンダリングする
	bottomIndex_++;
	if(bottomIndex_ == maxSpriteCount_){ render(); }
}
//------------------------------------------------------------------------------
// レンダリング
void SpriteGraphicsBuffer::render(){
	Assert((vertexBuffer_ != NULL) && (indexBuffer_ != NULL) &&
		(vertexDeclaration_ != NULL));
	Assert(topIndex_ <= bottomIndex_);

	// リクエストが無ければ何も行わない
	int spriteCount = bottomIndex_ - topIndex_;
	if(spriteCount == 0){ return; }

	// 頂点バッファの更新
	RenderingDevice* device = RenderingDevice::getInstance();
	int offset = sizeof(SpriteData) * topIndex_;
	int size = sizeof(SpriteData) * spriteCount;
	u_char* address = device->lockDynamicVertexBuffer(
		vertexBuffer_, offset, size);
	std::memcpy(address, &dataBuffer_[topIndex_], size);
	device->unlockDynamicVertexBuffer(vertexBuffer_);

	// 描画
	device->drawIndexedTriangleList(0, // BaseVertexIndex
		topIndex_ * vertexPerSprite_, // MinIndex
		spriteCount * vertexPerSprite_, // NumVertices
		topIndex_ * indexPerSprite_, // StartIndex
		spriteCount * 2);// PrimitiveCount

	// インデックスの更新
	if(bottomIndex_ == maxSpriteCount_){ topIndex_ = bottomIndex_ = 0; }
	else{ topIndex_ = bottomIndex_; }
}
//------------------------------------------------------------------------------
// セットアップ
void SpriteGraphicsBuffer::setup(){
	RenderingDevice* device = RenderingDevice::getInstance();
	// インデックスバッファの構築
	if(indexBuffer_ == NULL){ buildIndexBuffer(); }

	// 頂点バッファの構築
	if(vertexBuffer_ == NULL){
		int bufferSize = vertexBufferSize_ * vertexSize_;
		vertexBuffer_ = device->createDynamicVertexBuffer(bufferSize);
	}

	// 頂点記述の構築
	if(vertexDeclaration_ == NULL){
		TexCoord::Type texType(TexCoord::type2);
		int size = device->createVertexDeclaration(
			&vertexDeclaration_, true, 0, 0, false, false, 1, &texType);
		Assert(size == vertexSize_);
	}
	Assert((vertexBuffer_ != NULL) && (indexBuffer_ != NULL) &&
		(vertexDeclaration_ != NULL));

	// セットアップ
	device->setIndexBuffer(indexBuffer_);
	device->setVertexBuffer(vertexBuffer_, vertexSize_);
	device->setVertexDeclaration(vertexDeclaration_);
}
//------------------------------------------------------------------------------
// インデックスバッファの構築
void SpriteGraphicsBuffer::buildIndexBuffer(){
	// 静的インデックスバッファを作成する
	RenderingDevice* device = RenderingDevice::getInstance();
	int bufferSize = indexBufferSize_ * indexSize_;
	indexBuffer_ = device->createStaticIndexBuffer(bufferSize);

	// インデックス情報の書き込み
	u_short* address = (u_short*)device->lockStaticIndexBuffer(
		indexBuffer_, 0, bufferSize);
	u_short indexTable[indexPerSprite_] = { 0, 1, 2, 2, 1, 3};
	for(int i = 0; i < maxSpriteCount_; i++){
		int vertexOffset = i * vertexPerSprite_;
		for(int j = 0; j < indexPerSprite_; j++){
			(*address) = vertexOffset + indexTable[j];
			address++;
		}
	}
	device->unlockStaticIndexBuffer(indexBuffer_);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
