//------------------------------------------------------------------------------
// 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/RenderingDevice.h"
#include "Graphics/Renderer/Renderer.h"
#include "Graphics/System/GraphicsDeviceCapacity.h"
#include "Graphics/Fog/Fog.h"
#include "Graphics/Light/DirectionalLight.h"
#include "Graphics/Light/PointLight.h"
#include "Graphics/Material/Material.h"
#include "Graphics/Texture/Texture.h"

namespace Lamp{

// インスタンス
RenderingDevice* RenderingDevice::instance_ = NULL;
// 最大アクティブライト数
const int RenderingDevice::maxActiveLightCount_ =
	Renderer::maxActiveLightCount_;

//------------------------------------------------------------------------------
// システム
//------------------------------------------------------------------------------
// コンストラクタ
RenderingDevice::RenderingDevice(GraphicsDeviceCapacity* capacity) :
	capacity_(capacity), device_(NULL), defaultStateBlock_(NULL){
	Assert(instance_ == NULL);
	instance_ = this;
	resetCounter();
}
//------------------------------------------------------------------------------
// デストラクタ
RenderingDevice::~RenderingDevice(){
	// デフォルトステートブロック解放
	invalidateDefaultStateBlock();
	Assert(instance_ == this);
	instance_ = NULL;
}
//------------------------------------------------------------------------------
// ステート設定
//------------------------------------------------------------------------------
// レンダーステートの設定
void RenderingDevice::setRenderState(D3DRENDERSTATETYPE type, u_int state){
	DirectXCheck(device_->SetRenderState(type, state));
}
//------------------------------------------------------------------------------
// テクスチャステートの設定
void RenderingDevice::setTextureState(
	u_int stage, D3DTEXTURESTAGESTATETYPE type, u_int state){
	DirectXCheck(device_->SetTextureStageState(stage, type, state));
}
//------------------------------------------------------------------------------
// サンプラステートの設定
void RenderingDevice::setSamplerState(
	u_int stage, D3DSAMPLERSTATETYPE type, u_int state){
	DirectXCheck(device_->SetSamplerState(stage, type, state));
}
//------------------------------------------------------------------------------
// ステートブロック関連
//------------------------------------------------------------------------------
// ステートブロックの開始
void RenderingDevice::beginStateBlock(){
	DirectXCheck(device_->BeginStateBlock());
}
//------------------------------------------------------------------------------
// ステートブロックの終了
Direct3DStateBlock* RenderingDevice::endStateBlock(){
	Direct3DStateBlock* stateBlock;
	DirectXCheck(device_->EndStateBlock(&stateBlock));
	return stateBlock;
}
//------------------------------------------------------------------------------
// ステートブロックの適用
void RenderingDevice::applyStateBlock(Direct3DStateBlock* stateBlock){
	Assert(stateBlock != NULL);
	DirectXCheck(stateBlock->Apply());
}
//------------------------------------------------------------------------------
// デフォルトステートブロックのリストア
void RenderingDevice::restoreDefaultStateBlock(Direct3DDevice* direct3DDevice){
	device_ = direct3DDevice;
	// デフォルトステートブロックの構築
	DirectXCheck(device_->CreateStateBlock(D3DSBT_ALL, &defaultStateBlock_));
}
//------------------------------------------------------------------------------
// レンダーターゲット
//------------------------------------------------------------------------------
// レンダーターゲットサイズの取得
DimensionI RenderingDevice::getRenderTargetSize(){
	Direct3DSurface* surface;
	DirectXCheck(device_->GetRenderTarget(0, &surface));
	D3DSurfaceDescription description;
	DirectXCheck(surface->GetDesc(&description));
	DimensionI result(description.Width, description.Height);
	SafeRelease(surface);
	return result;
}
//------------------------------------------------------------------------------
// シーン関連
//------------------------------------------------------------------------------
// シーンの開始
bool RenderingDevice::beginScene(){
	// シーン開始
	if(DirectXFailed(device_->BeginScene())){ return false; }
	return true;
}
//------------------------------------------------------------------------------
// シーンの終了
void RenderingDevice::endScene(){
	DirectXCheck(device_->EndScene());
}
//------------------------------------------------------------------------------
// 行列関連
//------------------------------------------------------------------------------
// 投影行列の設定
void RenderingDevice::setProjectionMatrix(const Matrix44& projectionMatrix){
	Matrix44 transposeMatrix = projectionMatrix;
	transposeMatrix.transpose();
	DirectXCheck(device_->SetTransform(
		D3DTS_PROJECTION, (const D3DMATRIX*)transposeMatrix.array));
}
//------------------------------------------------------------------------------
// ビュー行列の設定
void RenderingDevice::setViewMatrix(const Matrix44& viewMatrix){
	Matrix44 transposeMatrix = viewMatrix;
	transposeMatrix.transpose();
	DirectXCheck(device_->SetTransform(
		D3DTS_VIEW, (const D3DMATRIX*)transposeMatrix.array));
}
//------------------------------------------------------------------------------
// ワールド行列の設定
void RenderingDevice::setWorldMatrix(const Matrix34& worldMatrix){
	Matrix44 worldMatrix44(
		worldMatrix.m00, worldMatrix.m10, worldMatrix.m20, 0.f,
		worldMatrix.m01, worldMatrix.m11, worldMatrix.m21, 0.f,
		worldMatrix.m02, worldMatrix.m12, worldMatrix.m22, 0.f,
		worldMatrix.m03, worldMatrix.m13, worldMatrix.m23, 1.f);
	DirectXCheck(device_->SetTransform(
		D3DTS_WORLD, (D3DMATRIX*)worldMatrix44.array));
}
//------------------------------------------------------------------------------
// テクスチャトランスフォーム設定
void RenderingDevice::setTextureTransform2(u_int stage,
	const TexCoord2& repeat, const TexCoord2& offset){
	if((repeat == TexCoord2(1.f, 1.f)) &&
		(offset == TexCoord2(0.f, 0.f))){
		setTextureState(stage, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
		return;
	}
	Matrix44 textureMatrix(
		repeat.u, 0.f, 0.f, 0.f,
		0.f, repeat.v, 0.f, 0.f,
		offset.u, offset.v, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f);
	DirectXCheck(device_->SetTransform(
		(D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + stage),
		(const D3DMATRIX*)textureMatrix.array));
	setTextureState(stage, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
}
//------------------------------------------------------------------------------
// ビューポート
//------------------------------------------------------------------------------
// ビューポートの設定
void RenderingDevice::setViewport(const RectangleI& rectangle){
	D3DViewport viewport;
	viewport.X = rectangle.x;
	viewport.Y = rectangle.y;
	viewport.Width = rectangle.width;
	viewport.Height = rectangle.height;
	viewport.MinZ = 0.f;
	viewport.MaxZ = 1.f;
	DirectXCheck(device_->SetViewport(&viewport));
}
//------------------------------------------------------------------------------
// ビューポートのリセット
void RenderingDevice::resetViewport(){
	RectangleI rectangle;
	DimensionI size = getRenderTargetSize();
	rectangle.set(0, 0, size.width, size.height);
	setViewport(rectangle);
}
//------------------------------------------------------------------------------
// ライティング関連
//------------------------------------------------------------------------------
// ライティングの設定
void RenderingDevice::setLighting(bool lighting){
	setRenderState(D3DRS_LIGHTING, lighting);
}
//------------------------------------------------------------------------------
// アンビエントカラーの設定
void RenderingDevice::setAmbientColor(const Color3f& ambientColor){
	Color4c color(ambientColor);
	setRenderState(D3DRS_AMBIENT, color.getARGB());
}
//------------------------------------------------------------------------------
// ディレクショナルライトを有効にする
void RenderingDevice::enableDirectionalLight(
	int lightIndex, DirectionalLight* directionalLight){
	Assert((lightIndex >= 0) && (lightIndex < maxActiveLightCount_));
	D3DLight light;
	::memset(&light, 0, sizeof(D3DLight));
	light.Type = D3DLIGHT_DIRECTIONAL;
	setD3DColor(light.Diffuse, directionalLight->getDiffuseColor());
	setD3DColor(light.Specular, directionalLight->getSpecularColor());
	setD3DVector(light.Direction, directionalLight->getWorldDirection());
	DirectXCheck(device_->LightEnable(lightIndex, true));
	DirectXCheck(device_->SetLight(lightIndex, &light));
}
//------------------------------------------------------------------------------
// ポイントライトを有効にする
void RenderingDevice::enablePointLight(
	int lightIndex, PointLight* pointLight){
	Assert((lightIndex >= 0) && (lightIndex < maxActiveLightCount_));
	D3DLight light;
	::memset(&light, 0, sizeof(D3DLight));
	light.Type = D3DLIGHT_POINT;
	setD3DColor(light.Diffuse, pointLight->getDiffuseColor());
	setD3DColor(light.Specular, pointLight->getSpecularColor());
	setD3DVector(light.Position, pointLight->getWorldPosition());
	light.Range = pointLight->getGlobalRange();
	light.Attenuation0 = pointLight->getAttenuation0();
	light.Attenuation1 = pointLight->getAttenuation1();
	light.Attenuation2 = pointLight->getAttenuation2();
	DirectXCheck(device_->LightEnable(lightIndex, true));
	DirectXCheck(device_->SetLight(lightIndex, &light));
}
//------------------------------------------------------------------------------
// ライトを閉じる
void RenderingDevice::closeLight(int lightIndex){
	for(int i = lightIndex; i < maxActiveLightCount_; i++){
		DirectXCheck(device_->LightEnable(i, false));
	}
}
//------------------------------------------------------------------------------
// マテリアル関連
//------------------------------------------------------------------------------
// マテリアルの設定
void RenderingDevice::setMaterial(
	const Color3f& diffuseColor, const Color3f& specularColor,
	const Color3f& ambientColor, const Color3f& emissiveColor,
	float specularPower, float alpha){
	// スペキュラの有無を設定
	if(specularColor == Color3f::black){
		setRenderState(D3DRS_SPECULARENABLE, false);
	}else{
		setRenderState(D3DRS_SPECULARENABLE, true);
	}
	// マテリアルを設定
	D3DMaterial material;
	setD3DColor(material.Diffuse, diffuseColor);
	material.Diffuse.a = alpha;
	setD3DColor(material.Specular, specularColor);
	material.Power = specularPower;
	setD3DColor(material.Ambient, ambientColor);
	setD3DColor(material.Emissive, emissiveColor);
	DirectXCheck(device_->SetMaterial(&material));
}
//------------------------------------------------------------------------------
// ブレンディングの設定
void RenderingDevice::setBlending(bool blendingFlag){
	setRenderState(D3DRS_ALPHABLENDENABLE, blendingFlag);
}
//------------------------------------------------------------------------------
// ブレンドモードの設定
void RenderingDevice::setBlendMode(int mode, int source, int destination){
	Assert((mode > 0) && (mode < Material::blendModeMax));
	Assert((source >= 0) && (source < Material::blendStateMax));
 	Assert((destination >= 0) && (destination < Material::blendStateMax));
	// ブレンドモード
	D3DBLENDOP blendModeTable[] = {
		(D3DBLENDOP)0,
		D3DBLENDOP_ADD,
		D3DBLENDOP_SUBTRACT,
		D3DBLENDOP_REVSUBTRACT,
		D3DBLENDOP_MIN,
		D3DBLENDOP_MAX,
	};
	setRenderState(D3DRS_BLENDOP, blendModeTable[mode]);
	// ブレンドステート
	D3DBLEND blendStateTable[] = {
		D3DBLEND_ZERO,
		D3DBLEND_ONE,
		D3DBLEND_SRCCOLOR,
		D3DBLEND_INVSRCCOLOR,
		D3DBLEND_SRCALPHA,
		D3DBLEND_INVSRCALPHA,
		D3DBLEND_SRCALPHASAT,
		D3DBLEND_DESTCOLOR,
		D3DBLEND_INVDESTCOLOR,
		D3DBLEND_DESTALPHA,
		D3DBLEND_INVDESTALPHA,
	};
	setRenderState(D3DRS_SRCBLEND, blendStateTable[source]);
	setRenderState(D3DRS_DESTBLEND, blendStateTable[destination]);
}
//------------------------------------------------------------------------------
// テクスチャ関連
//------------------------------------------------------------------------------
// テクスチャの設定
void RenderingDevice::setTexture(int textureStage, Texture* texture){
	DirectXCheck(device_->SetTexture(textureStage, texture->getD3DTexture()));
}
//------------------------------------------------------------------------------
// テクスチャの設定
void RenderingDevice::setTexture(int textureStage, Direct3DTexture* texture){
	DirectXCheck(device_->SetTexture(textureStage, texture));
}
//------------------------------------------------------------------------------
// アドレスモードの設定
void RenderingDevice::setTextureAddressMode2(int textureStage,
	int addressModeU, int addressModeV){
	// アドレスモードU
	if(addressModeU == Texture::addressModeWrap){
		setSamplerState(textureStage, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
	}else if(addressModeU == Texture::addressModeClamp){
		setSamplerState(textureStage, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
	}else{
		Assert(addressModeU == Texture::addressModeMirror);
		setSamplerState(textureStage, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
	}
	// アドレスモードV
	if(addressModeV == Texture::addressModeWrap){
		setSamplerState(textureStage, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
	}else if(addressModeV == Texture::addressModeClamp){
		setSamplerState(textureStage, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
	}else{
		Assert(addressModeV == Texture::addressModeMirror);
		setSamplerState(textureStage, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
	}
}
//------------------------------------------------------------------------------
// テクスチャの作成
Direct3DTexture* RenderingDevice::createTexture(
	D3DFORMAT format, int width, int height){
	Direct3DTexture* texture;
	DirectXCheck(device_->CreateTexture(width, height, 0, 0,
		format, D3DPOOL_MANAGED, &texture, NULL));
	return texture;
}
//------------------------------------------------------------------------------
// テクスチャのロック
D3DLOCKED_RECT RenderingDevice::lockTexture(
	Direct3DTexture* texture, int mipmapLevel){
	D3DLOCKED_RECT lockedRect;
	DirectXCheck(texture->LockRect(mipmapLevel, &lockedRect, NULL, 0));
	return lockedRect;
}
//------------------------------------------------------------------------------
// テクスチャのアンロック
void RenderingDevice::unlockTexture(Direct3DTexture* texture, int mipmapLevel){
	DirectXCheck(texture->UnlockRect(mipmapLevel));
}
//------------------------------------------------------------------------------
// テクスチャステージ関連
//------------------------------------------------------------------------------
// カラーテクスチャステージ設定
void RenderingDevice::setColorTextureStage(int colorStage,
	D3DTEXTUREOP operation, u_int arg1, u_int arg2, int uvIndex){
	setTextureState(colorStage, D3DTSS_COLOROP, operation);
	setTextureState(colorStage, D3DTSS_COLORARG1, arg1);
	setTextureState(colorStage, D3DTSS_COLORARG2, arg2);
	setTextureState(colorStage, D3DTSS_TEXCOORDINDEX, uvIndex);
}
//------------------------------------------------------------------------------
// カラーテクスチャステージを閉じる
void RenderingDevice::closeColorTextureStage(int colorStage){
	if(colorStage == 0){
		// カレントカラーを出力する
		setColorTextureStage(colorStage,
			D3DTOP_SELECTARG1, D3DTA_CURRENT, D3DTA_CURRENT, 0);
		colorStage++;
	}
	// カラーステージを閉じる
	setTextureState(colorStage, D3DTSS_COLOROP, D3DTOP_DISABLE);
}
//------------------------------------------------------------------------------
// アルファテクスチャステージ設定
void RenderingDevice::setAlphaTextureStage(int alphaTextureStage){
	if(alphaTextureStage == -1){
		// アルファテクスチャが無い場合
		setTextureState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
		setTextureState(0, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
		// アルファステージを閉じる
		setTextureState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		return;
	}
	// アルファテクスチャステージまでカレント出力を行う
	for(int i = 0; i < alphaTextureStage; i++){
		setTextureState(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
		setTextureState(i, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
	}
	// Current * alphaTexture
	setTextureState(alphaTextureStage, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
	setTextureState(alphaTextureStage, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
	setTextureState(alphaTextureStage, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
	// アルファステージを閉じる
	alphaTextureStage++;
	setTextureState(alphaTextureStage, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
}
//------------------------------------------------------------------------------
// フォグ
//------------------------------------------------------------------------------
// フォグの設定
void RenderingDevice::setFog(Fog* fog){
	if(fog->isEnabled()){
		setRenderState(D3DRS_FOGENABLE, true);
		setRenderState(D3DRS_FOGCOLOR, fog->getColor().getARGB());
		// 範囲フォグを有効にする
		setRenderState(D3DRS_RANGEFOGENABLE, true);
		// フォグモードにあわせた設定
		Fog::Mode fogMode = fog->getMode();
		if(fogMode == Fog::modeLinear){
			// 線形フォグ
			setRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
			float fogNear = fog->getNear();
			setRenderState(D3DRS_FOGSTART, *(u_int*)&fogNear);
			float fogFar = fog->getFar();
			setRenderState(D3DRS_FOGEND, *(u_int*)&fogFar);
		}else if(fogMode == Fog::modeExponent){
			// 指数フォグ
			setRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP);
			float fogDensity = fog->getDensity();
			setRenderState(D3DRS_FOGDENSITY , *(u_int*)&fogDensity);
		}else if(fogMode == Fog::modeExponent2){
			// 二乗の指数フォグ
			setRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP2);
			float fogDensity = fog->getDensity();
			setRenderState(D3DRS_FOGDENSITY , *(u_int*)&fogDensity);
		}
	}else{
		setRenderState(D3DRS_FOGENABLE, false);
	}
}
//------------------------------------------------------------------------------
// Zテスト
//------------------------------------------------------------------------------
// Zテストの設定
void RenderingDevice::setZTest(bool zTest){
	if(zTest){ setRenderState(D3DRS_ZENABLE, D3DZB_TRUE); }
	else{ setRenderState(D3DRS_ZENABLE, D3DZB_FALSE); }
}
//------------------------------------------------------------------------------
// インデックスバッファ
//------------------------------------------------------------------------------
// 静的インデックスバッファの構築
Direct3DIndexBuffer* RenderingDevice::createStaticIndexBuffer(int bufferSize){
	Assert(bufferSize > 0);
	Direct3DIndexBuffer* indexBuffer;
	DirectXCheck(device_->CreateIndexBuffer(
		bufferSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,
		D3DPOOL_MANAGED, &indexBuffer, NULL));
	Assert(indexBuffer != NULL);
	return indexBuffer;
}
//------------------------------------------------------------------------------
// 静的インデックスバッファのロック
u_char* RenderingDevice::lockStaticIndexBuffer(
	Direct3DIndexBuffer* indexBuffer, int offset, int size){
	Assert((indexBuffer != NULL) && (offset >= 0) && (size > 0));
	u_char* lockAddress;
	DirectXCheck(indexBuffer->Lock(offset, size, (void**)&lockAddress, 0));
	return lockAddress;
}
//------------------------------------------------------------------------------
// 静的インデックスバッファのアンロック
void RenderingDevice::unlockStaticIndexBuffer(
	Direct3DIndexBuffer* indexBuffer){
	Assert(indexBuffer != NULL);
	DirectXCheck(indexBuffer->Unlock());
}
//------------------------------------------------------------------------------
// 静的インデックスバッファの書き込み
void RenderingDevice::writeStaticIndexBuffer(
	Direct3DIndexBuffer* indexBuffer, const void* data, int dataSize){
	Assert(data != NULL);
	u_char* lockAddress = lockStaticIndexBuffer(indexBuffer, 0, dataSize);
	::memcpy(lockAddress, data, dataSize);
	unlockStaticIndexBuffer(indexBuffer);
}
//------------------------------------------------------------------------------
// 動的インデックスバッファの構築
Direct3DIndexBuffer* RenderingDevice::createDynamicIndexBuffer(int bufferSize){
	Assert(bufferSize > 0);
	Direct3DIndexBuffer* indexBuffer;
	DirectXCheck(device_->CreateIndexBuffer(
		bufferSize, (D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY), D3DFMT_INDEX16,
		D3DPOOL_DEFAULT, &indexBuffer, NULL));
	Assert(indexBuffer != NULL);
	return indexBuffer;
}
//------------------------------------------------------------------------------
// 動的インデックスバッファの書き込み
void RenderingDevice::writeDynamicIndexBuffer(
	Direct3DIndexBuffer* indexBuffer, const void* data, int dataSize){
	Assert(indexBuffer != NULL);
	Assert(data != NULL);
	Assert(dataSize > 0);
	u_char* lockAddress;
	DirectXCheck(indexBuffer->Lock(
		0, dataSize, (void**)&lockAddress, D3DLOCK_DISCARD));
	::memcpy(lockAddress, data, dataSize);
	DirectXCheck(indexBuffer->Unlock());
}
//------------------------------------------------------------------------------
// インデックスバッファの設定
void RenderingDevice::setIndexBuffer(Direct3DIndexBuffer* indexBuffer){
	DirectXCheck(device_->SetIndices(indexBuffer));
}
//------------------------------------------------------------------------------
// 頂点記述
//------------------------------------------------------------------------------
// 頂点記述の構築
int RenderingDevice::createVertexDeclaration(
	Direct3DVertexDeclaration** vertexDeclaration, bool hasPosition,
	int weightsPerVertex, int bonesPerVertex, bool hasNormal,
	bool hasColor, int texCoordSetCount,
	const TexCoord::Type* texCoordTypeArray){
	// 記述配列
	D3DVertexElement declarationArray[] = {
		D3DDECL_END(), // 位置
		D3DDECL_END(), // ウェイト
		D3DDECL_END(), // ボーンインデックス
		D3DDECL_END(), // 法線
		D3DDECL_END(), // カラー
		D3DDECL_END(), // テクスチャ座標0
		D3DDECL_END(), // テクスチャ座標1
		D3DDECL_END(), // テクスチャ座標2
		D3DDECL_END(), // テクスチャ座標3
		D3DDECL_END(), // テクスチャ座標4
		D3DDECL_END(), // テクスチャ座標5
		D3DDECL_END(), // テクスチャ座標6
		D3DDECL_END(), // テクスチャ座標7
		D3DDECL_END()
	};
	int vertexSize = 0;
	int declarationIndex = 0;

	// 位置の定義
	if(hasPosition){
		D3DVertexElement element = { 0, vertexSize, D3DDECLTYPE_FLOAT3,
			D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0};
		declarationArray[declarationIndex] = element;
		declarationIndex++;
		vertexSize += sizeof(float) * 3;
	}

	// ウェイトの定義
	if(weightsPerVertex != 0){
		Assert(false);
	}

	// ボーンインデックスの定義
	if(bonesPerVertex != 0){
		Assert(false);
	}

	// 法線の定義
	if(hasNormal){
		D3DVertexElement element = {
			0, vertexSize, D3DDECLTYPE_FLOAT3,
				D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0};
		declarationArray[declarationIndex] = element;
		declarationIndex++;
		vertexSize += sizeof(float) * 3;
	}

	// カラーの定義
	if(hasColor){
		D3DVertexElement element = {
			0, vertexSize, D3DDECLTYPE_D3DCOLOR,
				D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0};
		declarationArray[declarationIndex] = element;
		declarationIndex++;
		vertexSize += sizeof(u_int);
	}

	// テクスチャ座標の定義
	D3DDECLTYPE texCoordTypeTable[] = {
		D3DDECLTYPE_FLOAT1,// 使用しない
		D3DDECLTYPE_FLOAT1,
		D3DDECLTYPE_FLOAT2,
		D3DDECLTYPE_FLOAT3,
		D3DDECLTYPE_FLOAT4
	};
	for(int i = 0; i < texCoordSetCount; i++){
		int texCoordCount = texCoordTypeArray[i];
		Assert((texCoordCount > 0) && (texCoordCount <= 4));
		D3DVertexElement element = {
			0, vertexSize, texCoordTypeTable[texCoordCount],
			D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, i};
		declarationArray[declarationIndex] = element;
		declarationIndex++;
		vertexSize += sizeof(float) * texCoordCount;
	}
	// 頂点記述の構築
	DirectXCheck(device_->CreateVertexDeclaration(
		declarationArray, vertexDeclaration));
	Assert((*vertexDeclaration) != NULL);
	Assert(vertexSize != 0);
	return vertexSize;
}
//------------------------------------------------------------------------------
// 頂点記述の設定
void RenderingDevice::setVertexDeclaration(
	Direct3DVertexDeclaration* vertexDeclaration){
	DirectXCheck(device_->SetVertexDeclaration(vertexDeclaration));
}
//------------------------------------------------------------------------------
// 頂点バッファ
//------------------------------------------------------------------------------
// 静的頂点バッファの構築
Direct3DVertexBuffer* RenderingDevice::createStaticVertexBuffer(int bufferSize){
	Assert(bufferSize > 0);
	Direct3DVertexBuffer* vertexBuffer;
	DirectXCheck(device_->CreateVertexBuffer(
		bufferSize, D3DUSAGE_WRITEONLY, 0,
		D3DPOOL_MANAGED, &vertexBuffer, NULL));
	Assert(vertexBuffer != NULL);
	return vertexBuffer;
}
//------------------------------------------------------------------------------
// 静的頂点バッファの書き込み
void RenderingDevice::writeStaticVertexBuffer(
	Direct3DVertexBuffer* vertexBuffer, int bufferSize, int vertexCount,
	const Vector3* positions, int weightsPerVertex, const float* weights,
	int bonesBerVertex, const u_char* boneIndices, const Vector3* normals,
	const Color4c* colors, int texCoordSetCount,
	const TexCoord::Type* texCoordTypeArray, const float* const* texCoords){
	u_char* lockAddress;
	DirectXCheck(vertexBuffer->Lock(0, bufferSize, (void**)&lockAddress, 0));
	writeVertices(lockAddress, bufferSize, vertexCount, positions,
		weightsPerVertex, weights, bonesBerVertex, boneIndices, normals,
		colors, texCoordSetCount, texCoordTypeArray, texCoords);
	DirectXCheck(vertexBuffer->Unlock());
}
//------------------------------------------------------------------------------
// 動的頂点バッファの構築
Direct3DVertexBuffer* RenderingDevice::createDynamicVertexBuffer(
	int bufferSize){
	Assert(bufferSize > 0);
	Direct3DVertexBuffer* vertexBuffer;
	DirectXCheck(device_->CreateVertexBuffer(
		bufferSize, (D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY), 0,
		D3DPOOL_DEFAULT, &vertexBuffer, NULL));
	Assert(vertexBuffer != NULL);
	return vertexBuffer;
}
//------------------------------------------------------------------------------
// 動的頂点バッファのロック
u_char* RenderingDevice::lockDynamicVertexBuffer(
	Direct3DVertexBuffer* vertexBuffer, int offset, int size){
	u_int flag;
	if(offset == 0){ flag = D3DLOCK_DISCARD; }
	else{ flag = D3DLOCK_NOOVERWRITE; }
	u_char* lockAddress;
	DirectXCheck(vertexBuffer->Lock(offset, size, (void**)&lockAddress, flag));
	return lockAddress;
}
//------------------------------------------------------------------------------
// 動的頂点バッファのアンロック
void RenderingDevice::unlockDynamicVertexBuffer(
	Direct3DVertexBuffer* vertexBuffer){
	DirectXCheck(vertexBuffer->Unlock());
}
//------------------------------------------------------------------------------
// 動的頂点バッファの書き込み
void RenderingDevice::writeDynamicVertexBuffer(
	Direct3DVertexBuffer* vertexBuffer, int bufferSize, int vertexCount,
	const Vector3* positions, int weightsPerVertex, const float* weights,
	int bonesBerVertex, const u_char* boneIndices, const Vector3* normals,
	const Color4c* colors, int texCoordSetCount,
	const TexCoord::Type* texCoordTypeArray, const float* const* texCoords){
	u_char* lockAddress = lockDynamicVertexBuffer(vertexBuffer, 0, bufferSize);
	writeVertices(lockAddress, bufferSize, vertexCount, positions,
		weightsPerVertex, weights, bonesBerVertex, boneIndices, normals,
		colors, texCoordSetCount, texCoordTypeArray, texCoords);
	unlockDynamicVertexBuffer(vertexBuffer);
}
//------------------------------------------------------------------------------
// 頂点バッファの設定
void RenderingDevice::setVertexBuffer(
	Direct3DVertexBuffer* vertexBuffer, int vertexSize){
	DirectXCheck(device_->SetStreamSource(0, vertexBuffer, 0, vertexSize));
}
//------------------------------------------------------------------------------
// 描画
//------------------------------------------------------------------------------
// 三角リスト描画
void RenderingDevice::drawTriangleList(int primitiveCount){
	DirectXCheck(device_->DrawPrimitive(D3DPT_TRIANGLELIST, 0, primitiveCount));
}
//------------------------------------------------------------------------------
// インデックス三角リスト描画
void RenderingDevice::drawIndexedTriangleList(
	int vertexCount, int primitiveCount){
	DirectXCheck(device_->DrawIndexedPrimitive(
		D3DPT_TRIANGLELIST, 0, 0, vertexCount, 0, primitiveCount));
}
//------------------------------------------------------------------------------
// インデックス三角リスト描画
void RenderingDevice::drawIndexedTriangleList(int baseVertexIndex,
	int minIndex, int vertexCount, int startIndex, int primitiveCount){
	DirectXCheck(device_->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
		baseVertexIndex, minIndex, vertexCount, startIndex, primitiveCount));
}
//------------------------------------------------------------------------------
// ラインリスト描画
void RenderingDevice::drawLineList(int primitiveCount){
	DirectXCheck(device_->DrawPrimitive(D3DPT_LINELIST, 0, primitiveCount));
}
//------------------------------------------------------------------------------
// インデックスラインリスト描画
void RenderingDevice::drawIndexedLineList(
	int vertexCount, int primitiveCount){
	DirectXCheck(device_->DrawIndexedPrimitive(
		D3DPT_LINELIST, 0, 0, vertexCount, 0, primitiveCount));
}
//------------------------------------------------------------------------------
// ユーティリティ
//------------------------------------------------------------------------------
// 頂点データの書き込み
void RenderingDevice::writeVertices(
	u_char* buffer, int bufferSize, int vertexCount,
	const Vector3* positions, int weightsPerVertex, const float* weights,
	int bonesBerVertex, const u_char* boneIndices, const Vector3* normals,
	const Color4c* colors, int texCoordSetCount,
	const TexCoord::Type* texCoordTypeArray, const float* const* texCoords){
	Assert(buffer != NULL);
	Assert(bufferSize != 0);
	Assert(vertexCount != 0);
	bool hasPosition = (positions != NULL);
	bool hasWeights = (weightsPerVertex > 0);
	Assert(!hasWeights);
	bool hasBoneIndices = (bonesBerVertex > 0);
	Assert(!hasBoneIndices);
	bool hasNormal = (normals != NULL);
	bool hasColors = (colors != NULL);
	u_char* startAddress = buffer;
	for(int i = 0; i < vertexCount; i++){
		// 位置の書き込み
		if(hasPosition){
			(*(Vector3*)buffer) = (*positions);
			buffer += sizeof(Vector3);
			positions++;
		}
		// ウェイトの書き込み
		if(hasWeights){
		}
		// ボーンインデックスの書き込み
		if(hasBoneIndices){
		}
		// 法線の書き込み
		if(hasNormal){
			(*(Vector3*)buffer) = (*normals);
			buffer += sizeof(Vector3);
			normals++;
		}
		// 頂点カラーの書き込み
		if(hasColors){
			(*(u_int*)buffer) = (*colors).getARGB();
			colors++;
			buffer += sizeof(u_int);
		}

		// テクスチャ座標の書き込み
		for(int j = 0; j < texCoordSetCount; j++){
			int numFloat = texCoordTypeArray[j];
			int copySize = sizeof(float) * numFloat;
			::memcpy(buffer, texCoords[j] + (numFloat * i), copySize);
			buffer += copySize;
		}

	}
	Assert((buffer - startAddress) == bufferSize);
}
//------------------------------------------------------------------------------
// D3Dベクトルの設定
void RenderingDevice::setD3DVector(
	D3DVECTOR& destination, const Vector3& source){
	destination.x = source.x;
	destination.y = source.y;
	destination.z = source.z;
}
//------------------------------------------------------------------------------
// D3Dカラーの設定
void RenderingDevice::setD3DColor(
	D3DCOLORVALUE& destination, const Color4f& source){
	destination.r = source.r;
	destination.g = source.g;
	destination.b = source.b;
	destination.a = source.a;
}
//------------------------------------------------------------------------------
// D3Dカラーの設定
void RenderingDevice::setD3DColor(
	D3DCOLORVALUE& destination, const Color3f& source){
	destination.r = source.r;
	destination.g = source.g;
	destination.b = source.b;
	destination.a = 0.f;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
