#include "Mix/Tool/Win32/Core/Graphics/OptimizeMesh.h"

#include <algorithm>

#include "Mix/Tool/Win32/Core/Graphics/Manager.h"
#include "Mix/Tool/Win32/Core/Graphics/VertexLayout.h"
#include "Mix/Tool/Win32/Core/Graphics/VertexShader.h"
#include "Mix/Tool/Win32/Core/Graphics/PixelShader.h"
#include "Mix/Tool/Win32/Core/Graphics/VertexBuffer.h"
#include "Mix/Tool/Win32/Core/Graphics/IndexBuffer.h"
#include "Mix/Tool/Win32/Core/Graphics/DrawObject.h"
#include "Mix/Tool/Win32/Core/Graphics/ObjectModel.h"
#include "Mix/Tool/Win32/Core/Graphics/MaterialSlot.h"
#include "Mix/Tool/Win32/Core/Graphics/Material.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

OptimizeMesh::OptimizeMesh( Mix::Tool::Win32::Graphics::DrawObject* pDrawObject, Mix::Tool::Win32::Graphics::Node* pLinkNode ) :
m_pDrawObject( pDrawObject ),
m_pLinkNode( pLinkNode ),
m_VertexType( Mix::Tool::Win32::Graphics::VERTEX_UNKNOWN ),
m_IndexType( Mix::Tool::Win32::Graphics::INDEX_UNKNOWN ),
m_pVertexBuffer( NULL ),
m_pIndexBuffer( NULL )
{
	if( pDrawObject->GetType() == Mix::Tool::Win32::Object::GRAPHICS__OBJECT_MODEL )
	{
		m_pObjectModel = static_cast<Mix::Tool::Win32::Graphics::ObjectModel*>( pDrawObject );
	}
	else
	{
		m_pObjectModel = NULL;
	}
}

OptimizeMesh::~OptimizeMesh( void )
{
	MIX_RELEASE( m_pVertexBuffer );
	MIX_RELEASE( m_pIndexBuffer );
}

Mix::Tool::Win32::Graphics::DrawObject* OptimizeMesh::GetDrawObjectPtr( void ) const
{
	return m_pDrawObject;
}

Mix::Tool::Win32::Graphics::Node* OptimizeMesh::GetLinkNodePtr( void ) const
{
	return m_pLinkNode;
}

bool OptimizeMesh::GetBoneAvaliable( void ) const
{
	return false;
}

unsigned int OptimizeMesh::AddBone( const char* pName, const D3DXMATRIX& offsetMat )
{
	MIX_ERROR( L"AddBone はサポートしていません" );

	return 0;
}

unsigned int OptimizeMesh::AddBone( const wchar_t* pName, const D3DXMATRIX& offsetMat )
{
	MIX_ERROR( L"AddBone はサポートしていません" );

	return 0;
}

unsigned int OptimizeMesh::GetBoneCount( void ) const
{
	return 0;
}

const D3DXMATRIX* OptimizeMesh::GetBoneMatList( void ) const
{
	return NULL;
}

const OptimizeMesh::BONE* OptimizeMesh::GetBoneByIndex( unsigned int index ) const
{
	return NULL;
}

const OptimizeMesh::BONE* OptimizeMesh::GetBoneByName( const wchar_t* pName ) const
{
	return NULL;
}

void OptimizeMesh::ReservePolygonList( unsigned int count )
{
	m_PolygonList.clear();
	m_PolygonList.reserve( count );
}

void OptimizeMesh::AddPolygon( const Mix::Tool::Win32::Graphics::POLYGON& polygon )
{
	m_PolygonList.push_back( polygon );
}

bool OptimizeMesh::SetPolygonMaterial( int start, int count, int material )
{
	int size = static_cast<int>( m_PolygonList.size() );

	if( ( 0 > start ) ||
		( size <= start ) ||
		( size < ( start + count ) ) )
	{
		return false;
	}

	Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( m_PolygonList[start] );
	Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = ( pPolygon + count );

	while( pPolygon != pPolygonEnd )
	{
		pPolygon->materialNo = material;
		pPolygon++;
	}

	return true;
}

void OptimizeMesh::TransformPolygons( const D3DXMATRIX& mat )
{
	if( m_PolygonList.size() == 0 )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( m_PolygonList[0] );
	Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = ( pPolygon + m_PolygonList.size() );

	while( pPolygon != pPolygonEnd )
	{
		size_t vertexCount = pPolygon->vertices.size();

		if( vertexCount > 0 )
		{
			Mix::Tool::Win32::Graphics::VERTEX* pVertex = &( pPolygon->vertices[0] );
			Mix::Tool::Win32::Graphics::VERTEX* pVertexEnd = pVertex + vertexCount;

			D3DXVECTOR4 pos;
			D3DXVECTOR3 normal;

			while( pVertex != pVertexEnd )
			{
				D3DXVECTOR3* pPos = &( pVertex->pos );
				D3DXVECTOR3* pNormal = &( pVertex->normal );

				D3DXVec3Transform( &pos, pPos, &mat );
				D3DXVec3TransformNormal( &normal, pNormal, &mat );

				pPos->x = pos.x;
				pPos->y = pos.y;
				pPos->z = pos.z;

				pNormal->x = normal.x;
				pNormal->y = normal.y;
				pNormal->z = normal.z;

				pVertex++;
			}
		}

		pPolygon++;
	}
}

const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& OptimizeMesh::GetPolygonList( void ) const
{
	return m_PolygonList;
}

bool OptimizeMesh::Build( OptimizeMesh::BUILD_CONFIG& cfg )
{
	bool ret;

	switch( cfg.type )
	{
	case Mix::Tool::Win32::Graphics::MESH_BUILD_DIRECT:
		ret = Build_Direct( cfg );
		break;
	case Mix::Tool::Win32::Graphics::MESH_BUILD_OPTIMIZE:
		ret = Build_Optimize( cfg );
		break;
	}

	return ret;
}

bool OptimizeMesh::Build_Direct( OptimizeMesh::BUILD_CONFIG& cfg )
{
	MIX_ASSERT( m_PolygonList.size() > 0 );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 変数宣言
	////////////////////////////////////////////////////////////////////////////////////////////////////

	std::vector<OptimizeMesh::O_MATERIAL> oMaterialList;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リセット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_VertexType = Mix::Tool::Win32::Graphics::VERTEX_UNKNOWN;
	m_VertexList.clear();

	m_IndexType = Mix::Tool::Win32::Graphics::INDEX_UNKNOWN;
	m_IndexList.clear();

	m_SubsetList.clear();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ポリゴンをソート
	////////////////////////////////////////////////////////////////////////////////////////////////////

	std::sort( m_PolygonList.begin(), m_PolygonList.end(), Mix::Tool::Win32::Graphics::POLYGON() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ビルドの前処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Build_ProcessBefore( cfg, oMaterialList ) == false )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画データを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		OptimizeMesh::O_MATERIAL* pMaterial = &( oMaterialList[0] );
		OptimizeMesh::O_MATERIAL* pMaterialEnd = pMaterial + oMaterialList.size();

		unsigned int index = 0;

		while( pMaterial != pMaterialEnd )
		{
			OptimizeMesh::O_MATERIAL_BLOCK* pBlock = &( pMaterial->blockList[0] );
			OptimizeMesh::O_MATERIAL_BLOCK* pBlockEnd = pBlock + pMaterial->blockList.size();

			while( pBlock != pBlockEnd )
			{
				const Mix::Tool::Win32::Graphics::VERTEX* vertexList = &( pBlock->vertexList[0] );

				unsigned int indexStart = m_IndexList.size();

				OptimizeMesh::O_FACE* pFace = &( pBlock->faceList[0] );
				OptimizeMesh::O_FACE* pFaceEnd = pFace + pBlock->faceList.size();

				while( pFace != pFaceEnd )
				{
					unsigned int* pIndex = &( pFace->indices[0] );
					unsigned int* pIndexEnd = ( pIndex + 3 );

					pFace->indexOffset = m_IndexList.size();

					while( pIndex != pIndexEnd )
					{
						const Mix::Tool::Win32::Graphics::VERTEX& src = vertexList[*pIndex];

						OptimizeMesh::VERTEX dst;

						dst.pos = src.pos;
						dst.color = src.color;
						dst.normal = src.normal;
						dst.tangent = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
						dst.binormal = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
						dst.tex = src.tex;

						for( unsigned i = 0; i < MAX_VERTEX_WEIGHT; i++ )
						{
							dst.indices[i] = src.weights[i].index;
							dst.weights[i] = src.weights[i].value;
						}

						m_VertexList.push_back( dst );
						m_IndexList.push_back( index );

						*pIndex++ = index++;
					}

					pFace++;
				}

				pBlock->indexStart = indexStart;
				pBlock->indexCount = m_IndexList.size() - indexStart;

				pBlock++;
			}

			pMaterial++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ビルド後の処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Build_ProcessAfter( cfg, oMaterialList );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	return true;
}

bool OptimizeMesh::Build_Optimize( OptimizeMesh::BUILD_CONFIG& cfg )
{
	MIX_ASSERT( m_PolygonList.size() > 0 );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 変数宣言
	////////////////////////////////////////////////////////////////////////////////////////////////////

	std::vector<OptimizeMesh::O_MATERIAL> oMaterialList;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リセット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_VertexType = Mix::Tool::Win32::Graphics::VERTEX_UNKNOWN;
	m_VertexList.clear();

	m_IndexType = Mix::Tool::Win32::Graphics::INDEX_UNKNOWN;
	m_IndexList.clear();

	m_SubsetList.clear();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ポリゴンをソート
	////////////////////////////////////////////////////////////////////////////////////////////////////

	std::sort( m_PolygonList.begin(), m_PolygonList.end(), Mix::Tool::Win32::Graphics::POLYGON() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ビルドの前処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Build_ProcessBefore( cfg, oMaterialList ) == false )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画データの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	LogPrint( LT_INFO, L"      頂点の最適化" );

	{
		std::vector<Mix::Tool::Win32::Graphics::VERTEX> combineVertexList;

		unsigned int newIndex = 0;
		std::vector<OptimizeMesh::O_INDEX_INFO> indexInfoList;

		OptimizeMesh::O_MATERIAL* pMaterial = &( oMaterialList[0] );
		OptimizeMesh::O_MATERIAL* pMaterialEnd = pMaterial + oMaterialList.size();

		combineVertexList.reserve( 8 );

		while( pMaterial != pMaterialEnd )
		{
			unsigned int blockCount = 0;

			OptimizeMesh::O_MATERIAL_BLOCK* pBlock = &( pMaterial->blockList[0] );
			OptimizeMesh::O_MATERIAL_BLOCK* pBlockEnd = pBlock + pMaterial->blockList.size();

			LogPrint( LT_INFO, L"        Material[%s] : BlockCount[%d]",
				m_pDrawObject->GetMaterialSlotByIndex( pMaterial->index )->GetName(),
				pMaterial->blockList.size() );

			while( pBlock != pBlockEnd )
			{
				////////////////////////////////////////////////////////////////////////////////////////////////////

				unsigned int vertexCount = pBlock->vertexList.size();
				unsigned int optVertexCount = 0;

				OptimizeMesh::O_FACE* pFaceStart = &( pBlock->faceList[0] );
				OptimizeMesh::O_FACE* pFaceEnd = pFaceStart + pBlock->faceList.size();
				OptimizeMesh::O_FACE* pFace = NULL;

				unsigned int indexStart = m_IndexList.size();

				////////////////////////////////////////////////////////////////////////////////////////////////////

				/*
					最適化テーブルの作成
				*/

				pBlock->vertexOptTable.clear();
				pBlock->vertexOptTable.reserve( vertexCount * 2 );

				for( unsigned i = 0; i < vertexCount; i++ )
				{
					pBlock->vertexOptTable.push_back( i );
				}

				combineVertexList.clear();

				pFace = pFaceStart;
				while( pFace != pFaceEnd )
				{
					unsigned int* pIndex =  &( pFace->indices[0] );
					unsigned int* pIndexEnd = ( pIndex + 3 );

					while( pIndex != pIndexEnd )
					{
						if( *pIndex != pBlock->vertexOptTable[*pIndex] )
						{
							//すでに新規インデックスが割り振られているのでスキップ
							pIndex++;
							continue;
						}

						unsigned int newIndex = pBlock->vertexList.size();
						Mix::Tool::Win32::Graphics::VERTEX* pSrcVertex = &( pBlock->vertexList[*pIndex] );

						//結合用に自分を入れておく
						combineVertexList.clear();
						combineVertexList.push_back( *pSrcVertex );

						for( unsigned int i = 0; i < vertexCount/*pBlock->vertexList.size()*/; i++ )
						{
							if( i == ( *pIndex ) )
							{
								continue;
							}

							Mix::Tool::Win32::Graphics::VERTEX* pDstVertex = &( pBlock->vertexList[i] );

							if( ( pSrcVertex->pos == pDstVertex->pos ) &&
								( pSrcVertex->color == pDstVertex->color ) &&
								( pSrcVertex->tex == pDstVertex->tex ) )
							{
								if( cfg.bSmooting == false )
								{
									if( pSrcVertex->normal == pDstVertex->normal )
									{
										//相手に新規インデックスを割り振り、結合する頂点を追加
										pBlock->vertexOptTable[i] = newIndex;
										combineVertexList.push_back( *pDstVertex );
									}
								}
								else
								{
									float cosAngle = ::D3DXVec3Dot( &( pSrcVertex->normal ), &( pDstVertex->normal ) );

									if( cfg.smootingAngle <= cosAngle )
									{
										//相手に新規インデックスを割り振り、結合する頂点を追加
										pBlock->vertexOptTable[i] = newIndex;
										combineVertexList.push_back( *pDstVertex );
									}
								}
							}
/*
							if( ( pSrcVertex->pos == pDstVertex->pos ) &&
								( pSrcVertex->color == pDstVertex->color ) &&
								( pSrcVertex->tex == pDstVertex->tex ) &&
								( pSrcVertex->normal == pDstVertex->normal ) )
							{
								//相手に新規インデックスを割り振り、結合する頂点を追加
								pBlock->vertexOptTable[i] = newIndex;
								combineVertexList.push_back( *pDstVertex );
							}
*/
						}

						if( combineVertexList.size() > 1 )
						{
							Mix::Tool::Win32::Graphics::VERTEX* pCombineVertex = &( combineVertexList[0] );

							//スムージング
							if( cfg.bSmooting == true )
							{
								size_t combineVertexCount = combineVertexList.size();

								Mix::Tool::Win32::Graphics::VERTEX* pVertex = pCombineVertex;
								Mix::Tool::Win32::Graphics::VERTEX* pVertexEnd = pVertex + combineVertexCount;

								D3DXVECTOR3 combineNormal( 0.0f, 0.0f, 0.0f );

								while( pVertex != pVertexEnd )
								{
									::D3DXVec3Add( &combineNormal, &combineNormal, &( pVertex->normal ) );
									pVertex++;
								}

								combineNormal = combineNormal / static_cast<float>( combineVertexCount );
								::D3DXVec3Normalize( &( pCombineVertex->normal ), &combineNormal );
							}

							//自分に新規インデックスを割り振る
							pBlock->vertexOptTable[*pIndex] = newIndex;

							//結合した頂点を追加
							pBlock->vertexList.push_back( *pCombineVertex );
						}

						pIndex++;
					}

					pFace++;
				}

				////////////////////////////////////////////////////////////////////////////////////////////////////

				/*
					バーテックスリスト & インデックスリストの作成
				*/

				indexInfoList.clear();
				indexInfoList.resize( pBlock->vertexList.size(), OptimizeMesh::O_INDEX_INFO() );

				pFace = pFaceStart;
				while( pFace != pFaceEnd )
				{
					unsigned int* pIndex = &( pFace->indices[0] );
					unsigned int* pIndexEnd = ( pIndex + 3 );

					pFace->indexOffset = m_IndexList.size();

					while( pIndex != pIndexEnd )
					{
						unsigned int oldIndex  = pBlock->vertexOptTable[*pIndex];
						OptimizeMesh::O_INDEX_INFO* pIndexInfo = &( indexInfoList[oldIndex] );
					
						if( pIndexInfo->bAssign == false )
						{
							const Mix::Tool::Win32::Graphics::VERTEX src = pBlock->vertexList[oldIndex];
							OptimizeMesh::VERTEX dst;

							dst.pos = src.pos;
							dst.color = src.color;
							dst.normal = src.normal;
							dst.tangent = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
							dst.binormal = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
							dst.tex = src.tex;

							for( unsigned int i = 0; i < MAX_VERTEX_WEIGHT; i++ )
							{
								dst.indices[i] = src.weights[i].index;
								dst.weights[i] = src.weights[i].value;
							}

							pIndexInfo->value = newIndex;
							pIndexInfo->bAssign = true;

							m_VertexList.push_back( dst );
							m_IndexList.push_back( newIndex );

							newIndex++;
							optVertexCount++;
						}
						else
						{
							m_IndexList.push_back( pIndexInfo->value );
						}

						//「接線」「従法線」算出用に代入する
						*pIndex++ = pIndexInfo->value;
					}

					pFace++;
				}

				pBlock->indexStart = indexStart;
				pBlock->indexCount = m_IndexList.size() - indexStart;

				////////////////////////////////////////////////////////////////////////////////////////////////////

				LogPrint( LT_INFO, L"          Block[%d] : VertexCount[%d/%d]", blockCount, optVertexCount, vertexCount );

				blockCount++;
				pBlock++;
			}

			pMaterial++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ビルド後の処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Build_ProcessAfter( cfg, oMaterialList );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	return true;
}

bool OptimizeMesh::Build_ProcessBefore( OptimizeMesh::BUILD_CONFIG& cfg, std::vector<OptimizeMesh::O_MATERIAL>& materialList )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// イベント
	////////////////////////////////////////////////////////////////////////////////////////////////////

	OnBeforeBuild( cfg );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアルリストを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned int boneCount = GetBoneCount();

	if( boneCount > 0 )
	{
		LogPrint( LT_INFO, L"      ポリゴン、ボーンの割り当て" );
	}
	else
	{
		LogPrint( LT_INFO, L"      ポリゴンの割り当て" );
	}

	materialList.clear();

	{
		const Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( m_PolygonList[0] );
		const Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + m_PolygonList.size();

		unsigned int curMaterialNo = 0xFFFFFFFF;
		OptimizeMesh::O_MATERIAL* pCurMaterial = NULL;

		while( pPolygon != pPolygonEnd )
		{
			if( curMaterialNo != pPolygon->materialNo )
			{
				unsigned int polygonStart = ( pCurMaterial != NULL )? ( pCurMaterial->polygonStart + pCurMaterial->polygonCount ) : 0;

				curMaterialNo = pPolygon->materialNo;

				materialList.push_back( OptimizeMesh::O_MATERIAL() );
				pCurMaterial = &( materialList.back() );
				pCurMaterial->index = curMaterialNo;
				pCurMaterial->polygonStart = polygonStart;
				pCurMaterial->polygonCount = 0;
			}

			pCurMaterial->polygonCount++;

			pPolygon++;
		}
	}

	if( materialList.size() == 0 )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアルの処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( boneCount > 0 )
	{
		OptimizeMesh::O_MATERIAL* pMaterial = &( materialList[0] );
		OptimizeMesh::O_MATERIAL* pMaterialEnd = pMaterial + materialList.size();

		std::vector<OptimizeMesh::O_BONE_POLYGON_INFO> oBPInfoTable;
		std::vector<OptimizeMesh::O_BONE_POLYGON> oBPList;

		unsigned int boneOffset = 0;

		//作業用に確保
		oBPInfoTable.resize( boneCount );

		//作業用に適当に予約
		oBPList.reserve( m_PolygonList.size() );

		while( pMaterial != pMaterialEnd )
		{
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ログ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			LogPrint( LT_INFO, L"        Material[%s] : PolygonCount[%d]",
				m_pDrawObject->GetMaterialSlotByIndex( pMaterial->index )->GetName(),
				pMaterial->polygonCount );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ボーンポリゴンリストを作成
			////////////////////////////////////////////////////////////////////////////////////////////////////

			oBPList.clear();

			{
				const Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( m_PolygonList[pMaterial->polygonStart] );
				const Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + pMaterial->polygonCount;

				while( pPolygon != pPolygonEnd )
				{
					const Mix::Tool::Win32::Graphics::VERTEX* pVertex = &( pPolygon->vertices[0] );
					const Mix::Tool::Win32::Graphics::VERTEX* pVertexEnd = pVertex + pPolygon->vertices.size();

					unsigned int bpiCount = 0;
					OptimizeMesh::O_BONE_POLYGON* pBP = NULL;

					OptimizeMesh::O_BONE_POLYGON_INFO* pBPI;
					OptimizeMesh::O_BONE_POLYGON_INFO* pBPIEnd;

					ZeroMemory( &( oBPInfoTable[0] ), sizeof( OptimizeMesh::O_BONE_POLYGON_INFO ) * oBPInfoTable.size() );

					////////////////////////////////////////////////////////////////////////////////////////////////////

					while( pVertex != pVertexEnd )
					{
						const Mix::Tool::Win32::Graphics::WEIGHT* pWeight = &( pVertex->weights[0] );
						const Mix::Tool::Win32::Graphics::WEIGHT* pWeightEnd = pWeight + Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT;

						while( ( pWeight != pWeightEnd ) && ( pWeight->value > 0.0f ) )
						{
							OptimizeMesh::O_BONE_POLYGON_INFO* pBPI = &( oBPInfoTable[pWeight->index] );

							if( pBPI->keyCount == 0 )
							{
								pBPI->index = pWeight->index;

								bpiCount++;
								if( bpiCount > OptimizeMesh::BLOCK_BONE_MAX )
								{
									LogPrint( LT_ERROR, L"          ポリゴンが参照しているボーンの数が %d を超えています", OptimizeMesh::BLOCK_BONE_MAX );
									return false;
								}
							}

							pBPI->key += pWeight->value;

							pBPI->keyCount++;

							pWeight++;
						}

						pVertex++;
					}

					////////////////////////////////////////////////////////////////////////////////////////////////////

					pBPI = &( oBPInfoTable[0] );
					pBPIEnd = pBPI + bpiCount;

					//キー( ウェイト )の平均値を求める
					while( pBPI != pBPIEnd )
					{
						if( pBPI->keyCount > 0 )
						{
							pBPI->key = MIX_FLOAT_DIV( pBPI->key, static_cast<float>( pBPI->keyCount ) );
						}

						pBPI++;
					}

					//キー( ウェイト ) でソート
					std::sort( oBPInfoTable.begin(), oBPInfoTable.end(), OptimizeMesh::O_BONE_POLYGON_INFO() );

					////////////////////////////////////////////////////////////////////////////////////////////////////

					pBPI = &( oBPInfoTable[0] );
					pBPIEnd = pBPI + bpiCount;

					unsigned int* pKey;

					oBPList.push_back( OptimizeMesh::O_BONE_POLYGON() );
					pBP = &( oBPList.back() );

					pBP->keyCount = bpiCount;

					pKey = &( pBP->keys[0] );
					ZeroMemory( pKey, sizeof( unsigned int ) * OptimizeMesh::O_BONE_POLYGON_KEY_MAX );
					while( pBPI != pBPIEnd )
					{
						*pKey++ = pBPI->index;
						pBPI++;
					}

					pBP->pData = pPolygon;

					pPolygon++;
				}
			}

			//ボーンの周りに集まるようにソート
			std::sort( oBPList.begin(), oBPList.end(), OptimizeMesh::O_BONE_POLYGON() );
/*
			const OptimizeMesh::O_BONE_POLYGON* pBP = &( oBPList[0] );
			const OptimizeMesh::O_BONE_POLYGON* pBPEnd = pBP + oBPList.size();
			while( pBP != pBPEnd )
			{
				for( unsigned int i = 0; i < pBP->keyCount; i++ )
				{
					MIX_TRACE( L"%d ", pBP->keys[i] );
				}
				MIX_TRACELINE( L"" );

				pBP++;
			}

			MIX_TRACELINE( L"" );
*/

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ブロックリストを作成
			////////////////////////////////////////////////////////////////////////////////////////////////////

			{
				const OptimizeMesh::O_BONE_POLYGON* pBonePolygon = &( oBPList[0] );
				const OptimizeMesh::O_BONE_POLYGON* pBonePolygonEnd = pBonePolygon + oBPList.size();

				OptimizeMesh::O_MATERIAL_BLOCK* pBlock = NULL;

				unsigned int blockCount = 0;

				while(	( blockCount < OptimizeMesh::BLOCK_MAX ) &&
						( pBonePolygon != pBonePolygonEnd ) )
				{
					////////////////////////////////////////////////////////////////////////////////////////////////////

					if( pBlock == NULL )
					{
						pMaterial->blockList.push_back( OptimizeMesh::O_MATERIAL_BLOCK() );
						pBlock = &( pMaterial->blockList.back() );

						pBlock->boneCount = 0;
						pBlock->boneOffset = 0;
						pBlock->boneRefTable.resize( boneCount, 0 );
						pBlock->boneList.reserve( boneCount );
						pBlock->vertexList.reserve( pMaterial->polygonCount * 3 );
						pBlock->polygonList.reserve( pMaterial->polygonCount );
						pBlock->faceList.reserve( pMaterial->polygonCount );
						pBlock->indexStart = 0;
						pBlock->indexCount = 0;

						blockCount++;
					}

					////////////////////////////////////////////////////////////////////////////////////////////////////

					unsigned int blockBoneCount = pBlock->boneCount;
					unsigned int keyCount = pBonePolygon->keyCount;

					//ボーンを追加した際のボーン数を求める
					for( unsigned int i = 0; i < keyCount; i++ )
					{
						if( pBlock->boneRefTable[pBonePolygon->keys[i]] == 0 )
						{
							blockBoneCount++;
						}
					}

					if( blockBoneCount > OptimizeMesh::BLOCK_BONE_MAX )
					{
						//最大ボーン数を超えているので新たに追加する
						pBlock = NULL;
					}
					else
					{
						//更新
						pBlock->boneCount = blockBoneCount;
						pBlock->polygonList.push_back( *( pBonePolygon->pData ) );

						//ボーンをマーク
						for( unsigned int i = 0; i < keyCount; i++ )
						{
							pBlock->boneRefTable[pBonePolygon->keys[i]] = 0xFFFFFFFF;
						}

						//次のポリゴンへ
						pBonePolygon++;
					}

					////////////////////////////////////////////////////////////////////////////////////////////////////
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ブロックを完成させる
			////////////////////////////////////////////////////////////////////////////////////////////////////

			{
				OptimizeMesh::O_MATERIAL_BLOCK* pBlock = &( pMaterial->blockList[0] );
				OptimizeMesh::O_MATERIAL_BLOCK* pBlockEnd = pBlock + pMaterial->blockList.size();

				unsigned int blockCount = 0;

				while( pBlock != pBlockEnd )
				{
					////////////////////////////////////////////////////////////////////////////////////////////////////

					LogPrint( LT_INFO, L"          Block[%d] : BoneCount[%d] PolygonCount[%d]", blockCount, pBlock->boneCount, pBlock->polygonList.size() );

					////////////////////////////////////////////////////////////////////////////////////////////////////

					/*
						ボーン
					*/

					//参照テーブルとリスト
					for( unsigned int i = 0; i < pBlock->boneRefTable.size(); i++ )
					{
						if( pBlock->boneRefTable[i] == 0xFFFFFFFF )
						{
							unsigned int index = pBlock->boneList.size();

							pBlock->boneRefTable[i] = index;
							pBlock->boneList.push_back( i );

							LogPrint( LT_INFO, L"            Bone[%s] : Index[%d]", GetBoneByIndex( i )->name.c_str(), index );
						}
						else
						{
							pBlock->boneRefTable[i] = 0xFFFFFFFF;
						}
					}

					//オフセット
					pBlock->boneOffset = boneOffset;
					boneOffset += pBlock->boneList.size();

					////////////////////////////////////////////////////////////////////////////////////////////////////

					/*
						バーテックス、フェイスリストを作成
					*/

					Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( pBlock->polygonList[0] );
					Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + pBlock->polygonList.size();

					unsigned int vertexCount = 0;

					while( pPolygon != pPolygonEnd )
					{
						Mix::Tool::Win32::Graphics::VERTEX* pVertex = &( pPolygon->vertices[0] );
						Mix::Tool::Win32::Graphics::VERTEX* pVertexEnd = pVertex + pPolygon->vertices.size();

						while( pVertex != pVertexEnd )
						{
							pBlock->vertexList.push_back( *pVertex++ );
						}

						pBlock->faceList.push_back( OptimizeMesh::O_FACE( vertexCount, vertexCount + 1, vertexCount + 2 ) );

						vertexCount += 3;

						pPolygon++;
					}

					////////////////////////////////////////////////////////////////////////////////////////////////////

					/*
						バーテックスのボーンインデックスを変更
					*/

					const unsigned int* boneRefTable = &( pBlock->boneRefTable[0] );

					Mix::Tool::Win32::Graphics::VERTEX* pVertex = &( pBlock->vertexList[0] );
					Mix::Tool::Win32::Graphics::VERTEX* pVertexEnd = pVertex + pBlock->vertexList.size();

					while( pVertex != pVertexEnd )
					{
						Mix::Tool::Win32::Graphics::WEIGHT* pWeight = &( pVertex->weights[0] );
						Mix::Tool::Win32::Graphics::WEIGHT* pWeightEnd = pWeight + Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT;

						while( ( pWeight->value > 0.0f ) && ( pWeight != pWeightEnd ) )
						{
							unsigned int index = boneRefTable[pWeight->index];

							MIX_ASSERT( index != 0xFFFFFFFF );

							pWeight->index = index;
							pWeight++;
						}

						pVertex++;
					}

					blockCount++;
					pBlock++;
				}
			}

			pMaterial++;
		}
	}
	else
	{
		OptimizeMesh::O_MATERIAL* pMaterial = &( materialList[0] );
		OptimizeMesh::O_MATERIAL* pMaterialEnd = pMaterial + materialList.size();

		while( pMaterial != pMaterialEnd )
		{
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ログ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			LogPrint( LT_INFO, L"        Material[%s] : PolygonCount[%d]",
				m_pDrawObject->GetMaterialSlotByIndex( pMaterial->index )->GetName(),
				pMaterial->polygonCount );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ブロックを作成
			////////////////////////////////////////////////////////////////////////////////////////////////////

			OptimizeMesh::O_MATERIAL_BLOCK* pBlock;

			pMaterial->blockList.push_back( OptimizeMesh::O_MATERIAL_BLOCK() );
			pBlock = &( pMaterial->blockList.back() );

			pBlock->boneCount = 0;
			pBlock->boneOffset = 0;
			pBlock->polygonList.assign( m_PolygonList.begin() + pMaterial->polygonStart, m_PolygonList.begin() + pMaterial->polygonStart + pMaterial->polygonCount );
			pBlock->vertexList.reserve( pMaterial->polygonCount * 3 );
			pBlock->faceList.reserve( pMaterial->polygonCount );
			pBlock->indexStart = 0;
			pBlock->indexCount = 0;

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ポリゴンリスト & バーテックスリスト & フェイスリストの作成
			////////////////////////////////////////////////////////////////////////////////////////////////////

			const Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( pBlock->polygonList[0] );
			const Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + pBlock->polygonList.size();

			unsigned int vertexCount = 0;

			while( pPolygon != pPolygonEnd )
			{
				const Mix::Tool::Win32::Graphics::VERTEX* pVertex = &( pPolygon->vertices[0] );
				const Mix::Tool::Win32::Graphics::VERTEX* pVertexEnd = pVertex + pPolygon->vertices.size();

				while( pVertex != pVertexEnd )
				{
					pBlock->vertexList.push_back( *pVertex++ );
				}

				pBlock->faceList.push_back( OptimizeMesh::O_FACE( vertexCount, vertexCount + 1, vertexCount + 2 ) );

				vertexCount += 3;

				pPolygon++;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// ログ
			////////////////////////////////////////////////////////////////////////////////////////////////////

			LogPrint( LT_INFO, L"          Block[0] : PolygonCount[%d]", pBlock->polygonList.size() );

			pMaterial++;
		}
	}

	return true;
}

bool OptimizeMesh::Build_ProcessAfter( OptimizeMesh::BUILD_CONFIG& cfg, const std::vector<OptimizeMesh::O_MATERIAL>& oMaterialList )
{
	unsigned int boneCount = GetBoneCount();

	const OptimizeMesh::VERTEX* vertexList = &( m_VertexList[0] );
	const unsigned int* indexList = &( m_IndexList[0] );

	unsigned int indexStart = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画テーブルを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		const OptimizeMesh::O_MATERIAL* pMaterial = &( oMaterialList[0] );
		const OptimizeMesh::O_MATERIAL* pMaterialEnd = pMaterial + oMaterialList.size();

		while( pMaterial != pMaterialEnd )
		{
			const OptimizeMesh::O_MATERIAL_BLOCK* pBlock = &( pMaterial->blockList[0] );
			const OptimizeMesh::O_MATERIAL_BLOCK* pBlockEnd = pBlock + pMaterial->blockList.size();

			while( pBlock != pBlockEnd )
			{
				const OptimizeMesh::O_FACE* pFaceStart = &( pBlock->faceList[0] );
				const OptimizeMesh::O_FACE* pFaceEnd = pFaceStart + pBlock->faceList.size();
				const OptimizeMesh::O_FACE* pFace = NULL;

				OptimizeMesh::SUBSET* pDstSubset = NULL;

				//新規に追加
				m_SubsetList.push_back( OptimizeMesh::SUBSET() );
				pDstSubset = &( m_SubsetList.back() );

				//初期化
				pDstSubset->materialIndex = pMaterial->index;
				pDstSubset->draw.boneCount = pBlock->boneList.size();
				pDstSubset->draw.boneOffset = pBlock->boneOffset;
				pDstSubset->draw.vertexStart = 0xFFFFFFFF;
				pDstSubset->draw.vertexCount = 0;
				pDstSubset->draw.indexStart = 0xFFFFFFFF;
				pDstSubset->draw.indexCount = 0;

				////////////////////////////////////////////////////////////////////////////////////////////////////

				/*
					一括での描画
				*/

				{
					unsigned int vertexStart = 0xFFFFFFFF;
					unsigned int vertexEnd = 0;

					pFace = pFaceStart;
					while( pFace != pFaceEnd )
					{
						const unsigned int* pIndex = &( pFace->indices[0] );
						const unsigned int* pIndexEnd = pIndex + 3;

						while( pIndex != pIndexEnd )
						{
							if( vertexStart > *pIndex ) { vertexStart = *pIndex; }
							if( vertexEnd < *pIndex ) { vertexEnd = *pIndex; }

							pIndex++;
						}

						pFace++;
					}

					pDstSubset->draw.vertexStart = vertexStart;
					pDstSubset->draw.vertexCount = vertexEnd - vertexStart + 1;
					pDstSubset->draw.indexStart = pBlock->indexStart;
					pDstSubset->draw.indexCount = pBlock->indexCount;
					pDstSubset->draw.sortInfo = OptimizeMesh::CreateSortInfo( boneCount, pDstSubset->draw.vertexStart, pDstSubset->draw.vertexCount );
				}

				////////////////////////////////////////////////////////////////////////////////////////////////////

				/*
					ポリゴン単位での描画
				*/

				pFace = pFaceStart;
				while( pFace != pFaceEnd )
				{
					unsigned int i;
					unsigned int j;

					unsigned int vertexStart = 0xFFFFFFFF;
					unsigned int vertexEnd = 0;

					unsigned int boneEnd = 0;

					OptimizeMesh::DRAW draw;

					for( i = 0; i < 3; i++ )
					{
						unsigned int vertexIndex = pFace->indices[i];

						const unsigned char* boneIndices = &( vertexList[vertexIndex].indices[0] );
						const float* boneWeights = &( vertexList[vertexIndex].weights[0] );

						//頂点の開始、終了
						if( vertexStart > vertexIndex ) { vertexStart = vertexIndex; }
						if( vertexEnd < vertexIndex ) { vertexEnd = vertexIndex; }

						//ボーンの開始、終了
						for( j = 0; ( j < Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT ) && ( boneWeights[j] > 0.0f ); j++ )
						{
							unsigned int boneIndex = boneIndices[j];

							if( boneEnd < boneIndex ) { boneEnd = boneIndex; }
						}
					}

					//描画情報を追加
					draw.boneOffset = pBlock->boneOffset;
					draw.boneCount = pBlock->boneList.size();
					draw.vertexStart = vertexStart;
					draw.vertexCount = vertexEnd - vertexStart + 1;
					draw.indexStart = pFace->indexOffset;
					draw.indexCount = 3;
					draw.sortInfo = OptimizeMesh::CreateSortInfo( ( boneCount > 0 ), pFace, 1 );
					pDstSubset->drawList.push_back( draw );

					pFace++;
				}

				pBlock++;
			}

			pMaterial++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 頂点の接線空間を求める
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		unsigned int* pIndex = &( m_IndexList[0] );
		unsigned int* pIndexEnd = pIndex + m_IndexList.size();

		while( pIndex != pIndexEnd )
		{
			int i;
			float u[3];
			float v[3];

			OptimizeMesh::VERTEX* pVertex[3] =
			{
				&( m_VertexList[*( pIndex + 0 )] ),
				&( m_VertexList[*( pIndex + 1 )] ),
				&( m_VertexList[*( pIndex + 2 )] ),
			};

			D3DXVECTOR3 p0 = pVertex[0]->pos;
			D3DXVECTOR3 p1 = pVertex[1]->pos;
			D3DXVECTOR3 p2 = pVertex[2]->pos;

			D3DXVECTOR2 t0 = pVertex[0]->tex;
			D3DXVECTOR2 t1 = pVertex[1]->tex;
			D3DXVECTOR2 t2 = pVertex[2]->tex;

			D3DXVECTOR3 cp0[3] =
			{
				D3DXVECTOR3( p0.x, t0.x, t0.y ),
				D3DXVECTOR3( p0.y, t0.x, t0.y ),
				D3DXVECTOR3( p0.z, t0.x, t0.y ),
			};

			D3DXVECTOR3 cp1[3] =
			{
				D3DXVECTOR3( p1.x, t1.x, t1.y ),
				D3DXVECTOR3( p1.y, t1.x, t1.y ),
				D3DXVECTOR3( p1.z, t1.x, t1.y ),
			};

			D3DXVECTOR3 cp2[3] =
			{
				D3DXVECTOR3( p2.x, t2.x, t2.y ),
				D3DXVECTOR3( p2.y, t2.x, t2.y ),
				D3DXVECTOR3( p2.z, t2.x, t2.y ),
			};

			D3DXVECTOR3 v1;
			D3DXVECTOR3 v2;
			D3DXVECTOR3 abc;

			for( i = 0; i < 3; i++ )
			{
				v1 = cp1[i] - cp0[i];
				v2 = cp2[i] - cp1[i];

				::D3DXVec3Cross( &abc, &v1, &v2 );

				if( ( -FLT_EPSILON <= abc.x ) && ( FLT_EPSILON >= abc.x ) )
				{
					//縮退しているポリゴン
					u[i] = 0.0f;
					v[i] = 0.0f;
				}
				else
				{
					u[i] = ( abc.y / abc.x );
					v[i] = ( abc.z / abc.x );
				}
			}

			for( i = 0; i < 3; i++ )
			{
				pVertex[i]->tangent.x += u[0];
				pVertex[i]->tangent.y += u[1];
				pVertex[i]->tangent.z += u[2];
				::D3DXVec3Normalize( &( pVertex[i]->tangent ), &( pVertex[i]->tangent ) );

				pVertex[i]->binormal.x += v[0];
				pVertex[i]->binormal.y += v[1];
				pVertex[i]->binormal.z += v[2];
				::D3DXVec3Normalize( &( pVertex[i]->binormal ), &( pVertex[i]->binormal ) );
			}

			pIndex += 3;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 種類を決定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_VertexType = ( boneCount == 0 )? Mix::Tool::Win32::Graphics::VERTEX_SIMPLE : Mix::Tool::Win32::Graphics::VERTEX_BLEND;
	m_IndexType = ( m_IndexList.size() <= 65536 )? Mix::Tool::Win32::Graphics::INDEX_USHORT : Mix::Tool::Win32::Graphics::INDEX_UINT;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// イベント
	////////////////////////////////////////////////////////////////////////////////////////////////////

	OnAfterBuild( cfg, oMaterialList );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	LogPrint( LT_INFO, L"      リザルト" );
	LogPrint( LT_INFO, L"        VertexCount[%d/%d]", m_VertexList.size(), m_PolygonList.size() * 3 );
	LogPrint( LT_INFO, L"" );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	return true;
}

OptimizeMesh::DRAW_SORT_INFO OptimizeMesh::CreateSortInfo( bool bBoneEnabled, const OptimizeMesh::O_FACE* faceList, unsigned int faceCount )
{
	const OptimizeMesh::VERTEX* vertexList = &( m_VertexList[0] );
	const OptimizeMesh::O_FACE* pFace = &( faceList[0] );
	const OptimizeMesh::O_FACE* pFaceEnd = pFace + faceCount;

	OptimizeMesh::DRAW_SORT_INFO ret;

	ret.boneIndex = -1;
	ret.pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

	if( bBoneEnabled == true )
	{
		float boneBestWeight = 0.0f;

		while( pFace != pFaceEnd )
		{
			const unsigned int* pIndex = &( pFace->indices[0] );
			const unsigned int* pIndexEnd = pIndex + 3;

			while( pIndex != pIndexEnd )
			{
				const OptimizeMesh::VERTEX* pVertex = &( vertexList[*pIndex] );
				const unsigned char* boneIndices = &( pVertex->indices[0] );
				const float* boneWeights = &( pVertex->weights[0] );

				for( unsigned int i = 0; i < Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT; i++ )
				{
					if( boneBestWeight < boneWeights[i] )
					{
						ret.boneIndex = boneIndices[i];
						boneBestWeight = boneWeights[i];
					}
				}

				ret.pos += pVertex->pos;
				pIndex++;
			}

			pFace++;
		}
	}
	else
	{
		while( pFace != pFaceEnd )
		{
			const unsigned int* pIndex = &( pFace->indices[0] );
			const unsigned int* pIndexEnd = pIndex + 3;

			while( pIndex != pIndexEnd )
			{
				ret.pos += vertexList[*pIndex].pos;
				pIndex++;
			}

			pFace++;
		}
	}

	ret.pos /= static_cast<float>( faceCount * 3 );

	return ret;
}

OptimizeMesh::DRAW_SORT_INFO OptimizeMesh::CreateSortInfo( unsigned int boneCount, unsigned int vertexStart, unsigned int vertexCount )
{
	const OptimizeMesh::VERTEX* pVertex = &( m_VertexList[vertexStart] );
	const OptimizeMesh::VERTEX* pVertexEnd = pVertex + vertexCount;

	OptimizeMesh::DRAW_SORT_INFO ret;

	ret.boneIndex = -1;
	ret.pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

	if( boneCount > 0 )
	{
		float boneBestWeight = 0.0f;

		while( pVertex != pVertexEnd )
		{
			const unsigned char* boneIndices = &( pVertex->indices[0] );
			const float* boneWeights = &( pVertex->weights[0] );

			for( unsigned int i = 0; i < Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT; i++ )
			{
				if( boneBestWeight < boneWeights[i] )
				{
					ret.boneIndex = boneIndices[i];
					boneBestWeight = boneWeights[i];
				}
			}

			ret.pos += pVertex->pos;
			pVertex++;
		}
	}
	else
	{
		while( pVertex != pVertexEnd )
		{
			ret.pos += pVertex->pos;
			pVertex++;
		}
	}

	ret.pos /= static_cast<float>( vertexCount );

	return ret;
}

Mix::Tool::Win32::Graphics::VERTEX_TYPE OptimizeMesh::GetVertexType( void ) const
{
	return m_VertexType;
}

const std::vector<OptimizeMesh::VERTEX>& OptimizeMesh::GetVertexList( void ) const
{
	return m_VertexList;
}

Mix::Tool::Win32::Graphics::INDEX_TYPE OptimizeMesh::GetIndexType( void ) const
{
	return m_IndexType;
}

const std::vector<unsigned int>& OptimizeMesh::GetIndexList( void ) const
{
	return m_IndexList;
}

const std::vector<OptimizeMesh::SUBSET>& OptimizeMesh::GetSubsetList( void ) const
{
	return m_SubsetList;
}

void OptimizeMesh::Release( void )
{
	m_VertexType = Mix::Tool::Win32::Graphics::VERTEX_UNKNOWN;
	m_IndexType = Mix::Tool::Win32::Graphics::INDEX_UNKNOWN;

	m_VertexList.clear();
	m_IndexList.clear();

	m_SubsetList.clear();

	MIX_RELEASE( m_pVertexBuffer );
	MIX_RELEASE( m_pIndexBuffer );
}

bool OptimizeMesh::RefreshBuffer( void )
{
	MIX_ASSERT( m_VertexType != Mix::Tool::Win32::Graphics::VERTEX_UNKNOWN );
	MIX_ASSERT( m_IndexType != Mix::Tool::Win32::Graphics::INDEX_UNKNOWN );
	MIX_ASSERT( m_VertexList.size() > 0 );
	MIX_ASSERT( m_IndexList.size() > 0 );
	MIX_ASSERT( m_SubsetList.size() > 0 );

	Mix::Tool::Win32::Graphics::Manager* pManager;

	std::vector<OptimizeMesh::SUBSET>::iterator it_begin = m_SubsetList.begin();
	std::vector<OptimizeMesh::SUBSET>::iterator it_end = m_SubsetList.end();
	std::vector<OptimizeMesh::SUBSET>::iterator it;

	unsigned int vertexStride = 0;

	unsigned char* pBuffer = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 解放
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_RELEASE( m_pVertexBuffer );
	MIX_RELEASE( m_pIndexBuffer );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マネージャを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// バーテックスバッファのストライドを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( it = it_begin; it != it_end; ++it )
	{
		const OptimizeMesh::SUBSET* pSubset = &( *it );

		Mix::Tool::Win32::Graphics::Material* pMaterial = m_pDrawObject->GetMaterial( pSubset->materialIndex );
		if( pMaterial == NULL )
		{
			return false;
		}

		Mix::Tool::Win32::Graphics::VertexLayout* pVertexLayout = pMaterial->GetVertexLayout( m_VertexType );
		if( pVertexLayout == NULL )
		{
			return false;
		}

		unsigned int temp = pVertexLayout->GetStride();

		if( vertexStride < temp ) { vertexStride = temp; }
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// バーテックスバッファを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pVertexBuffer = pManager->CreateVertexBuffer( vertexStride, m_VertexList.size() );
	if( m_pVertexBuffer == NULL )
	{
		return false;
	}

	pBuffer = reinterpret_cast<unsigned char*>( m_pVertexBuffer->Lock() );
	if( pBuffer == NULL )
	{
		MIX_RELEASE( m_pVertexBuffer );
		return false;
	}

	for( it = it_begin; it != it_end; ++it )
	{
		const OptimizeMesh::SUBSET* pSubset = &( *it );

		Mix::Tool::Win32::Graphics::Material* pMaterial = m_pDrawObject->GetMaterial( pSubset->materialIndex );
		Mix::Tool::Win32::Graphics::VertexLayout* pVertexLayout = pMaterial->GetVertexLayout( m_VertexType );

		if( pVertexLayout != NULL )
		{
			unsigned int vertexStart = pSubset->draw.vertexStart;
			const OptimizeMesh::VERTEX* pSrc = &( m_VertexList[vertexStart] );
			unsigned char* pDst = pBuffer + ( vertexStride * vertexStart );

			unsigned int writeSize;

			writeSize = OptimizeMesh::WriteVertex( &( pVertexLayout->GetElementList()[0] ), pSrc, pDst, vertexStride, pSubset->draw.vertexCount );
			if( writeSize == 0 )
			{
				m_pVertexBuffer->Unlock();
				MIX_RELEASE( m_pVertexBuffer );
				return false;
			}
		}
		else
		{
			m_pVertexBuffer->Unlock();
			MIX_RELEASE( m_pVertexBuffer );
			return false;
		}
	}

	m_pVertexBuffer->Unlock();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インデックスバッファを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pIndexBuffer = pManager->CreateIndexBuffer( GetIndexType(), m_IndexList.size() );
	if( m_pIndexBuffer == NULL )
	{
		MIX_RELEASE( m_pVertexBuffer );
		return false;
	}

	if( m_IndexType == Mix::Tool::Win32::Graphics::INDEX_USHORT )
	{
		const unsigned int* pSrc = &( m_IndexList[0] );
		const unsigned int* pSrcEnd = pSrc + m_IndexList.size();
		unsigned short* pDst = NULL;

		pDst = reinterpret_cast<unsigned short*>( m_pIndexBuffer->Lock() );
		if( pDst != NULL )
		{
			while( pSrc != pSrcEnd )
			{
				*pDst++ = static_cast<unsigned short>( *pSrc++ );
			}

			m_pIndexBuffer->Unlock();
		}
		else
		{
			MIX_RELEASE( m_pVertexBuffer );
			MIX_RELEASE( m_pIndexBuffer );
			return false;
		}
	}
	else if( m_IndexType == Mix::Tool::Win32::Graphics::INDEX_UINT )
	{
		unsigned int* pDst = NULL;

		pDst = reinterpret_cast<unsigned int*>( m_pIndexBuffer->Lock() );
		if( pDst != NULL )
		{
			::CopyMemory( pDst, &( m_IndexList[0] ), sizeof( unsigned int ) * m_IndexList.size() );
			m_pIndexBuffer->Unlock();
		}
		else
		{
			MIX_RELEASE( m_pVertexBuffer );
			MIX_RELEASE( m_pIndexBuffer );
			return false;
		}
	}

	return true;
}

bool OptimizeMesh::RefreshBuffer( unsigned int materialSlotIndex )
{
	if( m_pDrawObject == NULL )
	{
		return false;
	}

	bool bFind = false;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// サブセットを調べる
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( std::vector<OptimizeMesh::SUBSET>::const_iterator it = m_SubsetList.begin(); ( it != m_SubsetList.end() ) && ( bFind == false ); ++it )
	{
		if( it->materialIndex == materialSlotIndex )
		{
			bFind = true;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// バッファを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( bFind == true )
	{
		RefreshBuffer();
	}

	return true;
}

void OptimizeMesh::OnDrawColor(	const D3DXMATRIX& viewMat,
								const D3DXMATRIX& worldMat,
								std::vector<Mix::Tool::Win32::Graphics::OPACITY_SUBSET>& opacitySubsets,
								std::vector<Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET>& transparencySubsets,
								std::vector<Mix::Tool::Win32::Graphics::REFRACT_SUBSET>& refractSubsets )
{
	if( ( GetVisible() == false ) ||
		( m_pVertexBuffer == NULL ) ||
		( m_pIndexBuffer == NULL ) )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::Manager* pManager;
	bool bProcRefract;
	D3DXMATRIX invWorldMat;

	::D3DXMatrixInverse( &invWorldMat, NULL, &worldMat );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マネージャを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 屈折マッピングが有効かどうかを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	bProcRefract = ( MIX_TEST_BIT( pManager->MEP_GetProcessFlags(), SHADER_PROCESS_REFRACT ) == SHADER_PROCESS_REFRACT );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画サブセットを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned int boneCount = GetBoneCount();
	const D3DXMATRIX* boneMatList = GetBoneMatList();

	std::vector<OptimizeMesh::SUBSET>::iterator it_subset_begin = m_SubsetList.begin();
	std::vector<OptimizeMesh::SUBSET>::iterator it_subset_end = m_SubsetList.end();
	std::vector<OptimizeMesh::SUBSET>::iterator it_subset;

	for( it_subset = it_subset_begin; it_subset != it_subset_end; ++it_subset )
	{
		const OptimizeMesh::SUBSET* pSubset = &( *it_subset );

		Mix::Tool::Win32::Graphics::Material* pMaterial;

		Mix::Tool::Win32::Graphics::VertexLayout* pCommonVL;
		Mix::Tool::Win32::Graphics::VertexShader* pColorVS;
		Mix::Tool::Win32::Graphics::PixelShader* pColorPS;
		Mix::Tool::Win32::Graphics::VertexShader* pMaskVS;
		Mix::Tool::Win32::Graphics::PixelShader* pMaskPS;
		Mix::Tool::Win32::Graphics::VertexShader* pSelectVS;
		Mix::Tool::Win32::Graphics::PixelShader* pSelectPS;

		pMaterial = m_pDrawObject->GetMaterial( pSubset->materialIndex );
		if( pMaterial == NULL )
		{
			continue;
		}

		pCommonVL = pMaterial->GetVertexLayout( m_VertexType );
		if( pCommonVL == NULL )
		{
			continue;
		}

		pColorVS = pMaterial->GetVertexShader( m_VertexType, Mix::Tool::Win32::Graphics::MAPPING_COLOR );
		if( pColorVS == NULL )
		{
			continue;
		}

		pColorPS = pMaterial->GetPixelShader( Mix::Tool::Win32::Graphics::MAPPING_COLOR );
		if( pColorPS == NULL )
		{
			continue;
		}

		pMaskVS = pMaterial->GetVertexShader( m_VertexType, Mix::Tool::Win32::Graphics::MAPPING_MASK );
		if( pMaskVS == NULL )
		{
			continue;
		}

		pMaskPS = pMaterial->GetPixelShader( Mix::Tool::Win32::Graphics::MAPPING_MASK );
		if( pMaskPS == NULL )
		{
			continue;
		}

		pSelectVS = pMaterial->GetSelectVertexShader( m_VertexType );
		if( pSelectVS == NULL )
		{
			continue;
		}

		pSelectPS = pMaterial->GetSelectPixelShader();
		if( pSelectPS == NULL )
		{
			continue;
		}

		if( pMaterial->GetTransparency() == false )
		{
			//不透明サブセット
			const OptimizeMesh::DRAW* pDraw = &( pSubset->draw );

			Mix::Tool::Win32::Graphics::OPACITY_SUBSET subset;

			if( boneCount == 0 )
			{
				subset.boneCount = 1;
				subset.boneMatTable = &( worldMat );
			}
			else
			{
				subset.boneCount = pDraw->boneCount;
				subset.boneMatTable = &( boneMatList[pDraw->boneOffset] );
			}

			subset.vertexStart = pDraw->vertexStart;
			subset.vertexNum = pDraw->vertexCount;
			subset.indexStart = pDraw->indexStart;
			subset.indexNum = pDraw->indexCount;
			subset.pMaterial = pMaterial;
			subset.pCommonVL = pCommonVL;
			subset.pColorVS = pColorVS;
			subset.pColorPS = pColorPS;
			subset.pSelectVS = pSelectVS;
			subset.pSelectPS = pSelectPS;
			subset.pVertexBuffer = m_pVertexBuffer;
			subset.pIndexBuffer = m_pIndexBuffer;
			subset.sortKey = pSubset->materialIndex;
			subset.selectColor = GetKeyColor();

			opacitySubsets.push_back( subset );
		}
		else
		{
			//半透明サブセット
			if( ( bProcRefract == true ) &&
				( pMaterial->GetShaderType() == Mix::Tool::Win32::Graphics::SHADER_WATER ) &&
				( pMaterial->GetBumpWaveType() == Mix::Tool::Win32::Graphics::WAVE_REFRACT ) )
			{
				//屈折
				const OptimizeMesh::DRAW* pDraw = &( pSubset->draw );

				Mix::Tool::Win32::Graphics::REFRACT_SUBSET subset;

				if( boneCount == 0 )
				{
					subset.boneCount = 1;
					subset.boneMatTable = &( worldMat );
				}
				else
				{
					subset.boneCount = pDraw->boneCount;
					subset.boneMatTable = &( boneMatList[pDraw->boneOffset] );
				}

				subset.vertexStart = pDraw->vertexStart;
				subset.vertexNum = pDraw->vertexCount;
				subset.indexStart = pDraw->indexStart;
				subset.indexNum = pDraw->indexCount;
				subset.pMaterial = pMaterial;
				subset.pCommonVL = pCommonVL;
				subset.pColorVS = pColorVS;
				subset.pColorPS = pColorPS;
				subset.pMaskVS = pMaskVS;
				subset.pMaskPS = pMaskPS;
				subset.pSelectVS = pSelectVS;
				subset.pSelectPS = pSelectPS;
				subset.pVertexBuffer = m_pVertexBuffer;
				subset.pIndexBuffer = m_pIndexBuffer;
				subset.sortKey = pSubset->materialIndex;
				subset.selectColor = GetKeyColor();

				refractSubsets.push_back( subset );
			}
			else
			{
				//通常
				Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD transparencyMethod = pMaterial->GetTransparencyMethod();
				D3DXMATRIX sortMat = ( boneCount == 0 )? worldMat * viewMat : viewMat;

				if( transparencyMethod == Mix::Tool::Win32::Graphics::TRANSPARENCY_LUMP )
				{
					//一括描画
					const OptimizeMesh::DRAW* pDraw = &( pSubset->draw );

					Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET subset;

					if( boneCount == 0 )
					{
						subset.boneCount = 1;
						subset.boneMatTable = &( worldMat );

						::D3DXVec3Transform( &( subset.sortKey ), &( pDraw->sortInfo.pos ), &sortMat );
					}
					else
					{
						D3DXMATRIX bsMat = boneMatList[pDraw->sortInfo.boneIndex] * sortMat;

						subset.boneCount = pDraw->boneCount;
						subset.boneMatTable = &( boneMatList[pDraw->boneOffset] );

						::D3DXVec3Transform( &( subset.sortKey ), &( pDraw->sortInfo.pos ), &bsMat );
					}

					subset.vertexStart = pDraw->vertexStart;
					subset.vertexNum = pDraw->vertexCount;
					subset.indexStart = pDraw->indexStart;
					subset.indexNum = pDraw->indexCount;
					subset.pMaterial = pMaterial;
					subset.pCommonVL = pCommonVL;
					subset.pColorVS = pColorVS;
					subset.pColorPS = pColorPS;
					subset.pSelectVS = pSelectVS;
					subset.pSelectPS = pSelectPS;
					subset.pVertexBuffer = m_pVertexBuffer;
					subset.pIndexBuffer = m_pIndexBuffer;
					subset.selectColor = GetKeyColor();

					transparencySubsets.push_back( subset );
				}
				else if( transparencyMethod == Mix::Tool::Win32::Graphics::TRANSPARENCY_INDIVIDUAL )
				{
					//ポリゴン単位での描画
					const OptimizeMesh::DRAW* pDraw = &( pSubset->drawList[0] );
					const OptimizeMesh::DRAW* pDrawEnd = pDraw + pSubset->drawList.size();

					while( pDraw != pDrawEnd )
					{
						Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET subset;

						if( boneCount == 0 )
						{
							subset.boneCount = 1;
							subset.boneMatTable = &( worldMat );

							::D3DXVec3Transform( &( subset.sortKey ), &( pDraw->sortInfo.pos ), &sortMat );
						}
						else
						{
							D3DXMATRIX bsMat = boneMatList[pDraw->sortInfo.boneIndex] * sortMat;

							subset.boneCount = pDraw->boneCount;
							subset.boneMatTable = &( boneMatList[pDraw->boneOffset] );

							::D3DXVec3Transform( &( subset.sortKey ), &( pDraw->sortInfo.pos ), &bsMat );
						}

						subset.vertexStart = pDraw->vertexStart;
						subset.vertexNum = pDraw->vertexCount;
						subset.indexStart = pDraw->indexStart;
						subset.indexNum = pDraw->indexCount;
						subset.pMaterial = pMaterial;
						subset.pCommonVL = pCommonVL;
						subset.pColorVS = pColorVS;
						subset.pColorPS = pColorPS;
						subset.pSelectVS = pSelectVS;
						subset.pSelectPS = pSelectPS;
						subset.pVertexBuffer = m_pVertexBuffer;
						subset.pIndexBuffer = m_pIndexBuffer;
						subset.selectColor = GetKeyColor();

						transparencySubsets.push_back( subset );

						pDraw++;
					}
				}
			}
		}
	}
}

void OptimizeMesh::OnDrawSelectMap(	const D3DXMATRIX& viewMat,
									const D3DXMATRIX& worldMat,
									std::vector<Mix::Tool::Win32::Graphics::OPACITY_SUBSET>& subsets )
{
	if( ( m_pVertexBuffer == NULL ) ||
		( m_pIndexBuffer == NULL ) ||
		( GetVisible() == false ) )
	{
		return;
	}

	unsigned int boneCount = GetBoneCount();
	const D3DXMATRIX* boneMatList = GetBoneMatList();

	std::vector<OptimizeMesh::SUBSET>::iterator it_subset_begin = m_SubsetList.begin();
	std::vector<OptimizeMesh::SUBSET>::iterator it_subset_end = m_SubsetList.end();
	std::vector<OptimizeMesh::SUBSET>::iterator it_subset;

	for( it_subset = it_subset_begin; it_subset != it_subset_end; ++it_subset )
	{
		const OptimizeMesh::SUBSET& srcSubset = ( *it_subset );
		const OptimizeMesh::DRAW& srcDraw = srcSubset.draw;

		Mix::Tool::Win32::Graphics::Material* pMaterial;

		Mix::Tool::Win32::Graphics::VertexLayout* pCommonVL;
		Mix::Tool::Win32::Graphics::VertexShader* pSelectVS;
		Mix::Tool::Win32::Graphics::PixelShader* pSelectPS;

		Mix::Tool::Win32::Graphics::OPACITY_SUBSET subset;

		pMaterial = m_pDrawObject->GetMaterial( srcSubset.materialIndex );
		if( pMaterial == NULL )
		{
			continue;
		}

		pCommonVL = pMaterial->GetVertexLayout( m_VertexType );
		if( pCommonVL == NULL )
		{
			continue;
		}

		pSelectVS = pMaterial->GetSelectVertexShader( m_VertexType );
		if( pSelectVS == NULL )
		{
			continue;
		}

		pSelectPS = pMaterial->GetSelectPixelShader();
		if( pSelectPS == NULL )
		{
			continue;
		}

		if( boneCount == 0 )
		{
			subset.boneCount = 1;
			subset.boneMatTable = &( worldMat );
		}
		else
		{
			subset.boneCount = srcDraw.boneCount;
			subset.boneMatTable = &( boneMatList[srcDraw.boneOffset] );
		}

		subset.vertexStart = srcDraw.vertexStart;
		subset.vertexNum = srcDraw.vertexCount;
		subset.indexStart = srcDraw.indexStart;
		subset.indexNum = srcDraw.indexCount;
		subset.pMaterial = pMaterial;
		subset.pCommonVL = pCommonVL;
		subset.pColorVS = NULL;
		subset.pColorPS = NULL;
		subset.pSelectVS = pSelectVS;
		subset.pSelectPS = pSelectPS;
		subset.pVertexBuffer = m_pVertexBuffer;
		subset.pIndexBuffer = m_pIndexBuffer;
		subset.sortKey = srcSubset.materialIndex;
		subset.selectColor = GetKeyColor();

		subsets.push_back( subset );
	}
}

void OptimizeMesh::OnDrawSelectColor(	const D3DXMATRIX& viewMat,
										const D3DXMATRIX& worldMat,
										std::vector<Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET>& subsets )
{
	if( ( m_pVertexBuffer == NULL ) ||
		( m_pIndexBuffer == NULL ) ||
		( GetVisible() == false ))
	{
		return;
	}

	Mix::Tool::Win32::Graphics::Manager* pManager;
	D3DXMATRIX invWorldMat;

	::D3DXMatrixInverse( &invWorldMat, NULL, &worldMat );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マネージャを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画サブセットを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned int boneCount = GetBoneCount();
	const D3DXMATRIX* boneMatList = ( boneCount > 0 )? GetBoneMatList() : NULL;

	std::vector<OptimizeMesh::SUBSET>::iterator it_subset_begin = m_SubsetList.begin();
	std::vector<OptimizeMesh::SUBSET>::iterator it_subset_end = m_SubsetList.end();
	std::vector<OptimizeMesh::SUBSET>::iterator it_subset;

	for( it_subset = it_subset_begin; it_subset != it_subset_end; ++it_subset )
	{
		const OptimizeMesh::SUBSET* pSubset = &( *it_subset );
		const OptimizeMesh::DRAW* pDraw = &( pSubset->draw );

		Mix::Tool::Win32::Graphics::Material* pMaterial;
		Mix::Tool::Win32::Graphics::VertexLayout* pCommonVL;
		Mix::Tool::Win32::Graphics::VertexShader* pSelectVS;
		Mix::Tool::Win32::Graphics::PixelShader* pSelectPS;

		Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET subset;

		pMaterial = m_pDrawObject->GetMaterial( pSubset->materialIndex );
		if( pMaterial == NULL )
		{
			continue;
		}

		pCommonVL = pMaterial->GetVertexLayout( m_VertexType );
		if( pCommonVL == NULL )
		{
			continue;
		}

		pSelectVS = pMaterial->GetSelectVertexShader( m_VertexType );
		if( pSelectVS == NULL )
		{
			continue;
		}

		pSelectPS = pMaterial->GetSelectPixelShader();
		if( pSelectPS == NULL )
		{
			continue;
		}

		if( boneCount == 0 )
		{
			subset.boneCount = 1;
			subset.boneMatTable = &( worldMat );
		}
		else
		{
			subset.boneCount = pDraw->boneCount;
			subset.boneMatTable = &( boneMatList[pDraw->boneOffset] );
		}

		subset.vertexStart = pDraw->vertexStart;
		subset.vertexNum = pDraw->vertexCount;
		subset.indexStart = pDraw->indexStart;
		subset.indexNum = pDraw->indexCount;
		subset.pMaterial = NULL;
		subset.pCommonVL = pCommonVL;
		subset.pColorVS = pSelectVS;
		subset.pColorPS = pSelectPS;
		subset.pVertexBuffer = m_pVertexBuffer;
		subset.pIndexBuffer = m_pIndexBuffer;
		::D3DXVec3Transform( &( subset.sortKey ), &( pDraw->sortInfo.pos ), &viewMat );

		subsets.push_back( subset );
	}
}

bool OptimizeMesh::WriteSimpleMaterial( Mix::Tool::Win32::File::OutputStream& output )
{
	Mix::Tool::Win32::Graphics::Manager* pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return false;
	}

	unsigned int procFlags = pManager->MEP_GetProcessFlags();

	std::vector<OptimizeMesh::SUBSET>::const_iterator it_begin = m_SubsetList.begin();
	std::vector<OptimizeMesh::SUBSET>::const_iterator it_end = m_SubsetList.end();
	std::vector<OptimizeMesh::SUBSET>::const_iterator it;

	OptimizeMesh::MESH_SIMPLE_OPACITY_SUBSET opSubset;
	std::vector<OptimizeMesh::MESH_SIMPLE_TRANSPARENCY_SUBSET> trSubsetList[Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX];

	for( it = it_begin; it != it_end; ++it )
	{
		const OptimizeMesh::SUBSET* pSubset = &( *it );

		Mix::Tool::Win32::Graphics::MaterialSlot* pSlot;

		bool bOpacity = ( MIX_TEST_BIT( procFlags, SHADER_PROCESS_SHADOW ) == SHADER_PROCESS_SHADOW );
		bool bTransparency[Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX] = { false, false };
		unsigned int materialNum;
		unsigned int i;

		OptimizeMesh::MESH_MATERIAL_DESC desc;

		//クリア
		trSubsetList[TRANSPARENCY_LUMP].clear();
		trSubsetList[TRANSPARENCY_INDIVIDUAL].clear();

		//スロットを取得
		pSlot = m_pDrawObject->GetMaterialSlotByIndex( pSubset->materialIndex );
		if( pSlot == NULL )
		{
			return false;
		}

		//マテリアル数を取得
		materialNum = pSlot->GetCount();

		//不透明サブセット、半透明サブセットを作成するかどうかを決定
		for( i = 0; i < materialNum; i++ )
		{
			Mix::Tool::Win32::Graphics::Material* pMaterial = pSlot->GetByIndex( i );
			if( pMaterial == NULL )
			{
				return false;
			}

			if( pMaterial->GetTransparency() == false )
			{
				bOpacity = true;
			}
			else
			{
				bTransparency[pMaterial->GetTransparencyMethod()] = true;
			}
		}

		//不透明サブセットを作成
		if( bOpacity == true )
		{
			opSubset.vertexStart = pSubset->draw.vertexStart;
			opSubset.vertexNum = pSubset->draw.vertexCount;
			opSubset.indexStart = pSubset->draw.indexStart;
			opSubset.indexNum = pSubset->draw.indexCount;
		}

		//半透明サブセット
		for( i = 0; i < Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX; i++ )
		{
			if( bTransparency[i] == false )
			{
				continue;
			}

			const OptimizeMesh::DRAW* pDraw = NULL;
			const OptimizeMesh::DRAW* pDrawEnd = NULL;

			std::vector<OptimizeMesh::MESH_SIMPLE_TRANSPARENCY_SUBSET>* pTrSubsetList = &( trSubsetList[i] );

			switch( i )
			{
			case Mix::Tool::Win32::Graphics::TRANSPARENCY_LUMP:
				pDraw = &( pSubset->draw );
				pDrawEnd = pDraw + 1;
				break;
			case Mix::Tool::Win32::Graphics::TRANSPARENCY_INDIVIDUAL:
				pDraw = &( pSubset->drawList[0] );
				pDrawEnd = pDraw + pSubset->drawList.size();
				break;
			}

			while( pDraw != pDrawEnd )
			{
				OptimizeMesh::MESH_SIMPLE_TRANSPARENCY_SUBSET subset;

				subset.vertexStart = pDraw->vertexStart;
				subset.vertexNum = pDraw->vertexCount;
				subset.indexStart = pDraw->indexStart;
				subset.indexNum = pDraw->indexCount;
				subset.sortKey[0] = pDraw->sortInfo.pos.x;
				subset.sortKey[1] = pDraw->sortInfo.pos.y;
				subset.sortKey[2] = pDraw->sortInfo.pos.z;
				subset.reserve = 0;

				pTrSubsetList->push_back( subset );

				pDraw++;
			}
		}

		//ヘッダを書き込む
		desc.slotIndex = pSubset->materialIndex;
		desc.opSubsetNum = ( bOpacity == true )? 1 : 0;
		desc.tlSubsetNum = trSubsetList[TRANSPARENCY_LUMP].size();
		desc.tiSubsetNum = trSubsetList[TRANSPARENCY_INDIVIDUAL].size();
		if( output.Write( &desc, sizeof( desc ) ) == false )
		{
			return false;
		}

		//不透明サブセットを書き込む
		if( bOpacity == true )
		{
			if( output.Write( &opSubset, sizeof( opSubset ) ) == false )
			{
				return false;
			}
		}

		//半透明サブセットを書き込む
		for( i = 0; i < Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX; i++ )
		{
			if( trSubsetList[i].size() == 0 )
			{
				continue;
			}

			if( output.Write( &( trSubsetList[i][0] ), sizeof( OptimizeMesh::MESH_SIMPLE_TRANSPARENCY_SUBSET ) * trSubsetList[i].size() ) == false )
			{
				return false;
			}
		}
	}

	return true;
}

bool OptimizeMesh::WriteBlendMaterial( Mix::Tool::Win32::File::OutputStream& output )
{
	Mix::Tool::Win32::Graphics::Manager* pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return false;
	}

	unsigned int procFlags = pManager->MEP_GetProcessFlags();

	std::vector<OptimizeMesh::SUBSET>::const_iterator it_begin = m_SubsetList.begin();
	std::vector<OptimizeMesh::SUBSET>::const_iterator it_end = m_SubsetList.end();
	std::vector<OptimizeMesh::SUBSET>::const_iterator it;

	OptimizeMesh::MESH_BLEND_OPACITY_SUBSET opSubset;
	std::vector<OptimizeMesh::MESH_BLEND_TRANSPARENCY_SUBSET> trSubsetList[Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX];

	for( it = it_begin; it != it_end; ++it )
	{
		const OptimizeMesh::SUBSET* pSubset = &( *it );

		Mix::Tool::Win32::Graphics::MaterialSlot* pSlot;

		bool bOpacity = ( MIX_TEST_BIT( procFlags, SHADER_PROCESS_SHADOW ) == SHADER_PROCESS_SHADOW );
		bool bTransparency[Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX] = { false, false };
		unsigned int materialNum;
		unsigned int i;

		OptimizeMesh::MESH_MATERIAL_DESC desc;

		//クリア
		trSubsetList[TRANSPARENCY_LUMP].clear();
		trSubsetList[TRANSPARENCY_INDIVIDUAL].clear();

		//スロットを取得
		pSlot = m_pDrawObject->GetMaterialSlotByIndex( pSubset->materialIndex );
		if( pSlot == NULL )
		{
			return false;
		}

		//マテリアル数を取得
		materialNum = pSlot->GetCount();

		//不透明サブセット、半透明サブセットを作成するかどうかを決定
		for( i = 0; i < materialNum; i++ )
		{
			Mix::Tool::Win32::Graphics::Material* pMaterial = pSlot->GetByIndex( i );
			if( pMaterial == NULL )
			{
				return false;
			}

			if( pMaterial->GetTransparency() == false )
			{
				bOpacity = true;
			}
			else
			{
				bTransparency[pMaterial->GetTransparencyMethod()] = true;
			}
		}

		//不透明サブセット
		if( bOpacity == true )
		{
			opSubset.boneOffset = pSubset->draw.boneOffset;
			opSubset.boneNum = pSubset->draw.boneCount;
			opSubset.vertexStart = pSubset->draw.vertexStart;
			opSubset.vertexNum = pSubset->draw.vertexCount;
			opSubset.indexStart = pSubset->draw.indexStart;
			opSubset.indexNum = pSubset->draw.indexCount;
		}

		//半透明サブセット
		for( i = 0; i < Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX; i++ )
		{
			if( bTransparency[i] == false )
			{
				continue;
			}

			const OptimizeMesh::DRAW* pDraw = NULL;
			const OptimizeMesh::DRAW* pDrawEnd = NULL;

			std::vector<OptimizeMesh::MESH_BLEND_TRANSPARENCY_SUBSET>* pTrSubsetList = &( trSubsetList[i] );

			switch( i )
			{
			case Mix::Tool::Win32::Graphics::TRANSPARENCY_LUMP:
				pDraw = &( pSubset->draw );
				pDrawEnd = pDraw + 1;
				break;
			case Mix::Tool::Win32::Graphics::TRANSPARENCY_INDIVIDUAL:
				pDraw = &( pSubset->drawList[0] );
				pDrawEnd = pDraw + pSubset->drawList.size();
				break;
			}

			while( pDraw != pDrawEnd )
			{
				OptimizeMesh::MESH_BLEND_TRANSPARENCY_SUBSET subset;

				subset.boneOffset = pDraw->boneOffset;
				subset.boneNum = pDraw->boneCount;
				subset.affectBoneIndex = pDraw->sortInfo.boneIndex;
				subset.vertexStart = pDraw->vertexStart;
				subset.vertexNum = pDraw->vertexCount;
				subset.indexStart = pDraw->indexStart;
				subset.indexNum = pDraw->indexCount;
				subset.sortKey[0] = pDraw->sortInfo.pos.x;
				subset.sortKey[1] = pDraw->sortInfo.pos.y;
				subset.sortKey[2] = pDraw->sortInfo.pos.z;

				pTrSubsetList->push_back( subset );

				pDraw++;
			}
		}

		//ヘッダを書き込む
		desc.slotIndex = pSubset->materialIndex;
		desc.opSubsetNum = ( bOpacity == true )? 1 : 0;
		desc.tlSubsetNum = trSubsetList[TRANSPARENCY_LUMP].size();
		desc.tiSubsetNum = trSubsetList[TRANSPARENCY_INDIVIDUAL].size();
		if( output.Write( &desc, sizeof( desc ) ) == false )
		{
			return false;
		}

		//不透明サブセットを書き込む
		if( bOpacity == true )
		{
			if( output.Write( &opSubset, sizeof( opSubset ) ) == false )
			{
				return false;
			}
		}

		//半透明サブセットを書き込む
		for( i = 0; i < Mix::Tool::Win32::Graphics::TRANSPARENCY_METHOD_MAX; i++ )
		{
			if( trSubsetList[i].size() == 0 )
			{
				continue;
			}

			if( output.Write( &( trSubsetList[i][0] ), sizeof( OptimizeMesh::MESH_BLEND_TRANSPARENCY_SUBSET ) * trSubsetList[i].size() ) == false )
			{
				return false;
			}
		}
	}

	return true;
}

bool OptimizeMesh::WriteVertexBuffer( Mix::Tool::Win32::File::OutputStream& output )
{
	if( m_pVertexBuffer == NULL )
	{
		return false;
	}

	return output.Write( m_pVertexBuffer->GetBuffer(), m_pVertexBuffer->GetBufferSize() );
}

bool OptimizeMesh::WriteIndexBuffer( Mix::Tool::Win32::File::OutputStream& output )
{
	if( m_pIndexBuffer == NULL )
	{
		return false;
	}

	return output.Write( m_pIndexBuffer->GetBuffer(), m_pIndexBuffer->GetBufferSize() );
}

bool OptimizeMesh::WriteVertex( const D3DVERTEXELEMENT9* pElements, const OptimizeMesh::VERTEX* pSrc, unsigned char* pDst, unsigned int stride, unsigned int count )
{
	const OptimizeMesh::VERTEX* pSrcEnd = pSrc + count;

	while( pSrc != pSrcEnd )
	{
		const D3DVERTEXELEMENT9* pElm = &( pElements[0] );
		unsigned char* pWrite = pDst;

		for( ;; )
		{
			if( ( pElm->Stream == 0xFF ) &&
				( pElm->Offset == 0 ) &&
				( pElm->Type == D3DDECLTYPE_UNUSED ) &&
				( pElm->Method == 0 ) &&
				( pElm->Usage == 0 ) &&
				( pElm->UsageIndex == 0 ) )
			{
				break;
			}

			unsigned int writeSize = 0;

			if( pElm->Usage == D3DDECLUSAGE_POSITION )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT3 )
				{
					//座標
					writeSize = sizeof( float[3] );
					::CopyMemory( pWrite, &( pSrc->pos ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_COLOR )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT4 )
				{
					//頂点カラー
					writeSize = sizeof( float[4] );
					::CopyMemory( pWrite, &( pSrc->color ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_NORMAL )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT3 )
				{
					//法線
					writeSize = sizeof( float[3] );
					::CopyMemory( pWrite, &( pSrc->normal ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_TANGENT )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT3 )
				{
					//接線
					writeSize = sizeof( float[3] );
					::CopyMemory( pWrite, &( pSrc->tangent ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_BINORMAL )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT3 )
				{
					//従法線
					writeSize = sizeof( float[3] );
					::CopyMemory( pWrite, &( pSrc->binormal ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_TEXCOORD )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT2 )
				{
					//テクスチャ座標
					writeSize = sizeof( float[2] );
					::CopyMemory( pWrite, &( pSrc->tex ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_BLENDINDICES )
			{
				if( pElm->Type == D3DDECLTYPE_UBYTE4 )
				{
					//ブレンドインデックス
					writeSize = sizeof( unsigned char[Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT] );
					::CopyMemory( pWrite, &( pSrc->indices[0] ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else if( pElm->Usage == D3DDECLUSAGE_BLENDWEIGHT )
			{
				if( pElm->Type == D3DDECLTYPE_FLOAT4 )
				{
					//ブレンドウェイト
					writeSize = sizeof( float[Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT] );
					::CopyMemory( pWrite, &( pSrc->weights[0] ), writeSize );
				}
				else
				{
					return false;
				}
			}
			else
			{
				return false;
			}

			pWrite += writeSize;

			pElm++;
		}

		pDst += stride;
		pSrc++;
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mesh
////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned int OptimizeMesh::GetPolygonCount( void ) const
{
	return m_PolygonList.size();
}

unsigned int OptimizeMesh::GetVertexCount( void ) const
{
	return m_VertexList.size();
}

unsigned int OptimizeMesh::GetIndexCount( void ) const
{
	return m_IndexList.size();
}

unsigned int OptimizeMesh::GetSubsetCount( void ) const
{
	return m_SubsetList.size();
}

unsigned int OptimizeMesh::GetMaterialSlotIndexBySubsetIndex( unsigned int subsetIndex ) const
{
	if( m_SubsetList.size() <= subsetIndex )
	{
		return 0;
	}

	return m_SubsetList[subsetIndex].materialIndex;
}

}}}}
