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

#include <sstream>
#include <algorithm>

#include "Mix/Tool/Win32/Core/Path.h"
#include "Mix/Tool/Win32/Core/Directory.h"

#include "Mix/Tool/Win32/Core/File/InputStream.h"
#include "Mix/Tool/Win32/Core/File/OutputStream.h"

#include "Mix/Tool/Win32/Core/Graphics/Manager.h"
#include "Mix/Tool/Win32/Core/Graphics/PolygonDivider.h"
#include "Mix/Tool/Win32/Core/Graphics/Material.h"
#include "Mix/Tool/Win32/Core/Graphics/BlankMesh.h"
#include "Mix/Tool/Win32/Core/Graphics/MapMesh.h"
#include "Mix/Tool/Win32/Core/Graphics/MapViewMesh.h"
#include "Mix/Tool/Win32/Core/Graphics/Node.h"
#include "Mix/Tool/Win32/Core/Graphics/Camera.h"

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

////////////////////////////////////////////////////////////////////////////////////////////////////
// OctreeModel : public
////////////////////////////////////////////////////////////////////////////////////////////////////

OctreeModel::OctreeModel( const wchar_t* pTitle, bool bRenderingOnly ) :
m_Title( ( pTitle != NULL )? pTitle : L"" ),
m_bRenderingOnly( bRenderingOnly ),
m_Name( L"" ),
m_SelectedMeshKey( 0 )
{
}

OctreeModel::~OctreeModel( void )
{
	if( m_OctNodeList.size() > 0 )
	{
		Octree_Release( m_OctNodeList[0] );
		m_OctNodeList.clear();
	}

	if( m_FileMeshList.size() > 0 )
	{
		for( OctreeModel::MeshList::iterator it = m_FileMeshList.begin(); it != m_FileMeshList.end(); ++it )
		{
			delete ( *it );
		}

		m_FileMeshList.clear();
	}

	if( m_NodeList.size() > 0 )
	{
		for( OctreeModel::NodeList::iterator it = m_NodeList.begin(); it != m_NodeList.end(); ++it )
		{
			delete ( *it );
		}

		m_NodeList.clear();
	}

	if( m_ViewMeshList.size() > 0 )
	{
		for( OctreeModel::ViewMeshList::iterator it = m_ViewMeshList.begin(); it != m_ViewMeshList.end(); ++it )
		{
			delete ( *it );
		}

		m_ViewMeshList.clear();
	}

	if( m_EditMeshList.size() > 0 )
	{
		for( OctreeModel::MeshList::iterator it = m_EditMeshList.begin(); it != m_EditMeshList.end(); ++it )
		{
			delete ( *it );
		}

		m_EditMeshList.clear();
	}
}

bool OctreeModel::Import( const OctreeModel::IMPORT_CONFIG& cfg, Mix::Tool::Win32::Graphics::BlankMesh* pBlankMesh )
{
	Mix::Tool::Win32::Graphics::FBX fbx;
	FBX::COMPUTING_TRANSFORMATION_MATRIX fbxCTM;

	OctreeModel::MeshList meshes;
	OctreeModel::MeshList::iterator it_mesh_begin;
	OctreeModel::MeshList::iterator it_mesh_end;
	OctreeModel::MeshList::iterator it_mesh;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 開始
	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		ログ
	*/

	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );
	LogPrint( LT_INFO, L" %sの作成 : FilePath[%s]", m_Title.c_str(), cfg.pFilePath );
	LogPrint( LT_INFO, L"" );

	/*
		名前を取得
	*/

	if( Mix::Tool::Win32::Path::GetFileNameWithoutExtension( cfg.pFilePath, m_Name ) == false )
	{
		return false;
	}

	/*
		インポート開始前を通知
	*/

	OnPreImport( cfg );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 読み込み
	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		FBXを読み込む
	*/

	switch( cfg.opt )
	{
	case Mix::Tool::Win32::Graphics::Model::IO_FBX_MAYA:
		fbxCTM = FBX::CTM_MAYA;
		break;
	case Mix::Tool::Win32::Graphics::Model::IO_FBX_3D_STUDIO_MAX:
		fbxCTM = FBX::CTM_3DS_MAX;
		break;
	default:
		fbxCTM = FBX::CTM_MAYA;
		break;
	}

	if( fbx.Load( cfg.pFilePath, fbxCTM, this ) == false )
	{
		return false;
	}

	/*
		ノードツリーと作業用メッシュリストを作成
	*/

	if( FBX_LoadNode( cfg, meshes, &fbx, fbx.GetRootNode(), NULL ) == true )
	{
		it_mesh_begin = meshes.begin();
		it_mesh_end = meshes.end();
	}
	else
	{
		ReleaseMeshes( meshes );
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ピボットスケールを適用
	////////////////////////////////////////////////////////////////////////////////////////////////////

	const D3DXVECTOR3& pivotScale = fbx.GetPivotScale();

	for( it_mesh = it_mesh_begin; it_mesh != it_mesh_end; ++it_mesh )
	{
		( *it_mesh )->ApplyPivotScaleToPolygon( pivotScale );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	D3DXMATRIX tempMat;
	D3DXMATRIX mat;

	D3DXMatrixIdentity( &mat );

	D3DXMatrixScaling( &mat, cfg.scaling, cfg.scaling, cfg.scaling );

	D3DXMatrixIdentity( &tempMat );
	mat *= *D3DXMatrixRotationX( &tempMat, D3DXToRadian( cfg.rotation.x ) );
	mat *= *D3DXMatrixRotationY( &tempMat, D3DXToRadian( cfg.rotation.y ) );
	mat *= *D3DXMatrixRotationZ( &tempMat, D3DXToRadian( cfg.rotation.z ) );

	mat._41 = cfg.translation.x;
	mat._42 = cfg.translation.y;
	mat._43 = cfg.translation.z;

	for( OctreeModel::MeshList::iterator it = meshes.begin(); it != meshes.end(); ++it )
	{
		( *it )->TransformPolygons( mat );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 最適化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		描画メッシュ
	*/

	if( Optimize( cfg, meshes ) == false )
	{
		LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );
		ReleaseMeshes( meshes );
		return false;
	}

	/*
		ブランクメッシュ
	*/

	if( pBlankMesh != NULL )
	{
		std::vector<Mix::Tool::Win32::Graphics::POLYGON> dstPolygonList;

		//ポリゴンをまとめる
		for( it_mesh = it_mesh_begin; it_mesh != it_mesh_end; ++it_mesh )
		{
			const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& srcPolygonList = ( *it_mesh )->GetPolygonList();
			dstPolygonList.insert( dstPolygonList.end(), srcPolygonList.begin(), srcPolygonList.end() );
		}

		//ビルド
		pBlankMesh->Build( dstPolygonList );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 後処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	ReleaseMeshes( meshes );

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

	return true;
}

bool OctreeModel::ImportFinalize( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアルを作成( 次の最適化でリフレッシュされるので、描画オブジェクトには通知しない )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	DrawObject::UpdateAllMaterialSlot();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュを作成( エディット )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		OctreeModel::MeshList::iterator it_begin = m_EditMeshList.begin();
		OctreeModel::MeshList::iterator it_end = m_EditMeshList.end();
		OctreeModel::MeshList::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			Mix::Tool::Win32::Graphics::MapMesh* pMesh = ( *it );

			if( pMesh->RefreshBuffer() == false )
			{
				return false;
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュを作成( ファイル )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_bRenderingOnly == false )
	{
		OctreeModel::MeshList::iterator it_begin = m_FileMeshList.begin();
		OctreeModel::MeshList::iterator it_end = m_FileMeshList.end();
		OctreeModel::MeshList::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			Mix::Tool::Win32::Graphics::MapMesh* pMesh = ( *it );

			if( pMesh->RefreshBuffer() == false )
			{
				return false;
			}
		}
	}

	return true;
}

const wchar_t* OctreeModel::GetName( void ) const
{
	return m_Name.c_str();
}

Mix::Tool::Win32::Graphics::Node* OctreeModel::GetRootNode( void )
{
	if( m_NodeList.size() == 0 )
	{
		return NULL;
	}

	return m_NodeList[0];
}

void OctreeModel::SelectedMeshByKey( unsigned int key )
{
	m_SelectedMeshKey = key;
}

void OctreeModel::SelectedNode( Mix::Tool::Win32::Graphics::Node* pNode )
{
	if( pNode != NULL )
	{
		if( pNode->GetAttribute() == Node::MESH )
		{
			m_SelectedMeshKey = pNode->GetMeshPtr()->GetKey();
		}
		else
		{
			m_SelectedMeshKey = 0;
		}
	}
	else
	{
		m_SelectedMeshKey = 0;
	}
}

bool OctreeModel::SaveModel( void )
{
	return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// OctreeModel : Private
////////////////////////////////////////////////////////////////////////////////////////////////////

void OctreeModel::OnDraw( DrawObject::DRAW_EVENT_ARGS& args )
{
	if( m_OctNodeList.size() == 0 )
	{
		return;
	}

	Draw( GetWorldMatrix(), m_OctNodeList[0], args );
}

void OctreeModel::OnRefreshMesh( unsigned int materialSlotIndex )
{
	for( OctreeModel::MeshList::iterator it = m_EditMeshList.begin(); it != m_EditMeshList.end(); ++it )
	{
		( *it )->RefreshBuffer( materialSlotIndex );
	}
}

bool OctreeModel::Optimize( const OctreeModel::IMPORT_CONFIG& cfg, const OctreeModel::MeshList& meshes )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 変数宣言
	////////////////////////////////////////////////////////////////////////////////////////////////////

	OctreeModel::MeshList::const_iterator it_m_begin = meshes.begin();
	OctreeModel::MeshList::const_iterator it_m_end = meshes.end();
	OctreeModel::MeshList::const_iterator it_m;

	OctreeModel::ViewMeshList::const_iterator it_vm_begin = m_ViewMeshList.begin();
	OctreeModel::ViewMeshList::const_iterator it_vm_end = m_ViewMeshList.end();
	OctreeModel::ViewMeshList::const_iterator it_vm;

	Mix::Tool::Win32::Geometry::AABB aabb;
	Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pRootNode;
	unsigned int meshIndex;
	unsigned int meshKey;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 八分木を作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	LogPrint( LT_INFO, L"" );
	LogPrint( LT_INFO, L"  八分木の初期化" );

	/*
		モデルを囲む AABB を求める
	*/

	aabb.min = D3DXVECTOR3( +FLT_MAX, +FLT_MAX, +FLT_MAX );
	aabb.max = D3DXVECTOR3( -FLT_MAX, -FLT_MAX, -FLT_MAX );

	for( it_m = it_m_begin; it_m != it_m_end; ++it_m )
	{
		const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& polygons = ( *it_m )->GetPolygonList();
		const Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( polygons[0] );
		const Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + polygons.size();

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

			while( pVertex != pVertexEnd )
			{
				const D3DXVECTOR3& pos = pVertex->pos;

				if( aabb.min.x > pos.x ){ aabb.min.x = pos.x; }
				if( aabb.min.y > pos.y ){ aabb.min.y = pos.y; }
				if( aabb.min.z > pos.z ){ aabb.min.z = pos.z; }

				if( aabb.max.x < pos.x ){ aabb.max.x = pos.x; }
				if( aabb.max.y < pos.y ){ aabb.max.y = pos.y; }
				if( aabb.max.z < pos.z ){ aabb.max.z = pos.z; }

				pVertex++;
			}

			pPolygon++;
		}
	}

	/*
		指定されたセルサイズまで AABB を分割して初期のオクトツリーを作成
	*/

	pRootNode = Octree_Create( cfg.cellSize, cfg.cellError, NULL, aabb );

	/*
		八分木にポリゴンを挿入
	*/

	meshIndex = 0;

	for( it_m = it_m_begin; it_m != it_m_end; ++it_m )
	{
		const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& polygons = ( *it_m )->GetPolygonList();
		const Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( polygons[0] );
		const Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + polygons.size();

		while( pPolygon != pPolygonEnd )
		{
			Octree_InsertPolygon( meshIndex, pRootNode, *pPolygon );

			pPolygon++;
		}

		meshIndex++;
	}

	/*
		八分木の最適化
	*/

	//不要なノードの削除
	Octree_Optimize1( pRootNode );

	//AABBを切り詰める
	Octree_Optimize2( pRootNode );

	/*
		八分木のファイナライズ
	*/

	Octree_Finalize( m_OctNodeList, pRootNode );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ビューメッシュにキーを割り振る
	////////////////////////////////////////////////////////////////////////////////////////////////////

	meshKey = 1;

	for( it_vm = it_vm_begin; it_vm != it_vm_end; ++it_vm )
	{
		Mix::Tool::Win32::Graphics::MapViewMesh* pViewMesh = ( *it_vm );

		pViewMesh->SetKey( meshKey );
		meshKey++;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュの最適化( エディット )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	LogPrint( LT_INFO, L"" );
	LogPrint( LT_INFO, L"  メッシュの最適化( エディット )" );

	{
		unsigned int meshCount = m_EditMeshList.size();
		unsigned int meshIndex = 1;
		unsigned int vertexCount = 0;
		unsigned int indexCount = 0;

		OctreeModel::MeshList::iterator it_begin = m_EditMeshList.begin();
		OctreeModel::MeshList::iterator it_end = m_EditMeshList.end();
		OctreeModel::MeshList::iterator it;

		OptimizeMesh::BUILD_CONFIG buildCfg;

		buildCfg.type = cfg.meshBuildType;
		buildCfg.bSmooting = cfg.bSmooting;
		buildCfg.smootingAngle = ::cosf( D3DX_PI * cfg.smootingAngle / 180.0f );

		for( it = it_begin; it != it_end; ++it, meshIndex++ )
		{
			Mix::Tool::Win32::Graphics::MapMesh* pMesh = ( *it );

			LogPrint( LT_INFO, L"    ブロック[%d/%d] : Key[%d] PolygonCount[%d]", meshIndex, meshCount, pMesh->GetKey(), pMesh->GetPolygonCount() );

			//最適化
			if( pMesh->Build( buildCfg ) == false )
			{
				return false;
			}

			//ログ用のカウント
			vertexCount += pMesh->GetVertexCount();
			indexCount += pMesh->GetIndexCount();
		}

		LogPrint( LT_INFO, L"    リザルト : VertexCount[%d/%d] PolygonCount[%d]", vertexCount, indexCount, indexCount / 3 );
		LogPrint( LT_INFO, L"" );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュの最適化( ファイル )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_bRenderingOnly == false )
	{
		LogPrint( LT_INFO, L"" );
		LogPrint( LT_INFO, L"  メッシュの最適化( ファイル )" );

		unsigned int meshCount = m_FileMeshList.size();
		unsigned int meshIndex = 1;
		unsigned int vertexCount = 0;
		unsigned int indexCount = 0;

		OctreeModel::MeshList::iterator it_begin = m_FileMeshList.begin();
		OctreeModel::MeshList::iterator it_end = m_FileMeshList.end();
		OctreeModel::MeshList::iterator it;

		OptimizeMesh::BUILD_CONFIG buildCfg;

		buildCfg.type = cfg.meshBuildType;
		buildCfg.bSmooting = cfg.bSmooting;
		buildCfg.smootingAngle = ::cosf( D3DX_PI * cfg.smootingAngle / 180.0f );

		for( it = it_begin; it != it_end; ++it, meshIndex++ )
		{
			Mix::Tool::Win32::Graphics::MapMesh* pMesh = ( *it );

			LogPrint( LT_INFO, L"    ブロック[%d/%d] : PolygonCount[%d]", meshIndex, meshCount, pMesh->GetPolygonCount() );

			//最適化
			if( pMesh->Build( buildCfg ) == false )
			{
				return false;
			}

			//ログ用のカウント
			vertexCount += pMesh->GetVertexCount();
			indexCount += pMesh->GetIndexCount();
		}

		LogPrint( LT_INFO, L"    リザルト : VertexCount[%d/%d] PolygonCount[%d]", vertexCount, indexCount, indexCount / 3 );
		LogPrint( LT_INFO, L"" );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ライト回転用ボリュームの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		Mix::Tool::Win32::Geometry::SPHERE rlVolume;

		rlVolume.pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

		if( m_OctNodeList.size() > 0 )
		{
			float rMin = ::D3DXVec3Length( &( m_OctNodeList[0]->aabb.min ) );
			float rMax = ::D3DXVec3Length( &( m_OctNodeList[0]->aabb.max ) );

			rlVolume.radius = max( rMin, rMax );
		}
		else
		{
			rlVolume.radius = 100.0f;
		}

		SetRotationLightVolume( rlVolume );
	}

	return true;
}

void OctreeModel::Draw( const D3DXMATRIX& worldMat, const OctreeModel::OCT_NODE* pNode, DrawObject::DRAW_EVENT_ARGS& args )
{
	std::vector<OctreeModel::OCT_NODE*>::const_iterator it_child_begin = pNode->childs.begin();
	std::vector<OctreeModel::OCT_NODE*>::const_iterator it_child_end = pNode->childs.end();
	std::vector<OctreeModel::OCT_NODE*>::const_iterator it_child;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 視界チェック
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( args.pCamera->Contains( pNode->aabb ) == false )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュを処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pNode->editMeshes.size() > 0 )
	{
		if( MIX_TEST_BIT( args.flags, DrawObject::DF_BODY ) == DrawObject::DF_BODY )
		{
			/*
				ボディの描画
			*/

			OctreeModel::MeshList::const_iterator it_em_begin = pNode->editMeshes.begin();
			OctreeModel::MeshList::const_iterator it_em_end = pNode->editMeshes.end();
			OctreeModel::MeshList::const_iterator it_em;

			if( m_bRenderingOnly == false )
			{
				for( it_em = it_em_begin; it_em != it_em_end; ++it_em )
				{
					Mix::Tool::Win32::Graphics::MapMesh* pEditMesh = ( *it_em );

					/*
						カラーのレンダリング
					*/

					pEditMesh->OnDrawColor(	args.pCamera->GetViewMatrix(),
											worldMat,
											*( args.pOpacitySubsets ),
											*( args.pTransparencySubsets ),
											*( args.pRefractSubsets ) );

					/*
						セレクトマップのレンダリング
					*/

					pEditMesh->OnDrawSelectMap( args.pCamera->GetViewMatrix(), worldMat, *( args.pSelectMapSubsets ) );

					/*
						セレクトカラーのレンダリング
					*/

					if( pEditMesh->GetKey() == m_SelectedMeshKey )
					{
						pEditMesh->OnDrawSelectColor( args.pCamera->GetViewMatrix(), worldMat, *( args.pSelectSubsets ) );
					}
				}
			}
			else
			{
				for( it_em = it_em_begin; it_em != it_em_end; ++it_em )
				{
					Mix::Tool::Win32::Graphics::MapMesh* pEditMesh = ( *it_em );

					/*
						カラーのレンダリング
					*/

					pEditMesh->OnDrawColor(	args.pCamera->GetViewMatrix(),
											worldMat,
											*( args.pOpacitySubsets ),
											*( args.pTransparencySubsets ),
											*( args.pRefractSubsets ) );
				}
			}
		}

		if( MIX_TEST_BIT( args.flags, DrawObject::DF_VIEWVOLUME ) == DrawObject::DF_VIEWVOLUME )
		{
			if( m_bRenderingOnly == false )
			{
				/*
					ビューボリュームの描画
				*/

				args.pLineHelper->SetMatrix( worldMat );
				args.pLineHelper->SetColor( args.viewVolumeColor );
				args.pLineHelper->AddAABB( pNode->aabb );
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 子を処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( it_child = it_child_begin; it_child != it_child_end; ++it_child )
	{
		Draw( worldMat, ( *it_child ), args );
	}
}

Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* OctreeModel::Octree_Create(	const D3DXVECTOR3& cellSize,
																				const float cellError,
																				Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pParentNode,
																				const Mix::Tool::Win32::Geometry::AABB& aabb )
{
	Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode;
	D3DXVECTOR3 aabbSize;
	D3DXVECTOR3 center;
	Mix::Tool::Win32::Geometry::AABB aabbList[8];
	Mix::Tool::Win32::Geometry::AABB tmp;
	int aabbCount = 0;
	int i;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	//ノードを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pNode = new Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE( pParentNode, aabb );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	//親のAABBを分割して、子のAABBを作成する
	////////////////////////////////////////////////////////////////////////////////////////////////////

	aabbSize = ( aabb.max - aabb.min );

	center = ( ( aabb.min + aabb.max ) * 0.5f );

	aabbList[0] = aabb;
	aabbCount = 1;

	//YZ平面(左右分割)でAABBを分割
	if( ( aabbSize.x > cellSize.x ) &&
		( ::fabs( aabbSize.x - cellSize.x ) >= cellError ) )
	{
		tmp = aabbList[0];

		aabbList[0].max.x = center.x;

		aabbList[1] = tmp;
		aabbList[1].min.x = center.x;

		aabbCount += aabbCount;

		pNode->splitOrder[pNode->splitOrderCount++] = OctreeModel::OCT_NODE::SO_YZ;
	}

	//XZ平面(上下分割)でAABBを分割
	if( ( aabbSize.y > cellSize.y ) &&
		( ::fabs( aabbSize.y - cellSize.y ) >= cellError ) )
	{
		int writeOffset = aabbCount;

		for( i = 0; i < aabbCount; i++ )
		{
			tmp = aabbList[i];

			aabbList[i].max.y = center.y;

			aabbList[writeOffset + i] = tmp;
			aabbList[writeOffset + i].min.y = center.y;
		}

		aabbCount += aabbCount;

		pNode->splitOrder[pNode->splitOrderCount++] = OctreeModel::OCT_NODE::SO_XZ;
	}

	//XY平面(前後分割)でAABBを分割
	if( ( aabbSize.z > cellSize.z ) &&
		( ::fabs( aabbSize.z - cellSize.z ) >= cellError ) )
	{
		int writeOffset = aabbCount;

		for( i = 0; i < aabbCount; i++ )
		{
			tmp = aabbList[i];

			aabbList[i].max.z = center.z;

			aabbList[writeOffset + i] = tmp;
			aabbList[writeOffset + i].min.z = center.z;
		}

		aabbCount += aabbCount;

		pNode->splitOrder[pNode->splitOrderCount++] = OctreeModel::OCT_NODE::SO_XY;
	}

	if( aabbCount > 1 )
	{
		//子も処理する
		for( i = 0; i < aabbCount; i++ )
		{
			Octree_Create( cellSize, cellError, pNode, aabbList[i] );
		}
	}

	return pNode;
}

void OctreeModel::Octree_InsertPolygon(	unsigned int viewMeshIndex,
										Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode,
										const Mix::Tool::Win32::Graphics::POLYGON& polygon )
{
#ifdef _DEBUG
	{
		bool bError = false;
		D3DXVECTOR3 minError = D3DXVECTOR3( FLT_MAX, FLT_MAX, FLT_MAX );
		D3DXVECTOR3 maxError = D3DXVECTOR3( FLT_MIN, FLT_MIN, FLT_MIN );

		const D3DXVECTOR3 mi = pNode->aabb.min;
		const D3DXVECTOR3 ma = pNode->aabb.max;
		for( std::vector<Mix::Tool::Win32::Graphics::VERTEX>::const_iterator it = polygon.vertices.begin(); it != polygon.vertices.end(); ++it )
		{
			const D3DXVECTOR3& v = ( *it ).pos;

			if( mi.x > v.x )
			{
				float err = ( v.x - mi.x );
				if( minError.x > err ){ minError.x = err; }
				bError = true;
			}

			if( mi.y > v.y )
			{
				float err = ( v.y - mi.y );
				if( minError.y > err ){ minError.y = err; }
				bError = true;
			}

			if( mi.z > v.z )
			{
				float err = ( v.z - mi.z );
				if( minError.z > err ){ minError.z = err; }
				bError = true;
			}

			if( ma.x < v.x )
			{
				float err = ( v.x - ma.x );
				if( maxError.x < err ){ maxError.x = err; }
				bError = true;
			}

			if( ma.y < v.y )
			{
				float err = ( v.y - ma.y );
				if( maxError.y < err ){ maxError.y = err; }
				bError = true;
			}

			if( ma.z < v.z )
			{
				float err = ( v.z - ma.z );
				if( maxError.z < err ){ maxError.z = err; }
				bError = true;
			}
		}

		if( bError == true )
		{
			wchar_t temp[1024];

			if( minError.x >= FLT_MAX ){ minError.x = 0.0f; }
			if( minError.y >= FLT_MAX ){ minError.y = 0.0f; }
			if( minError.z >= FLT_MAX ){ minError.z = 0.0f; }

			if( maxError.x <= FLT_MIN ){ maxError.x = 0.0f; }
			if( maxError.y <= FLT_MIN ){ maxError.y = 0.0f; }
			if( maxError.z <= FLT_MIN ){ maxError.z = 0.0f; }

			if( ( minError.x <= -0.001f ) ||
				( minError.y <= -0.001f ) ||
				( minError.z <= -0.001f ) ||
				( maxError.x >= 0.001f ) ||
				( maxError.y >= 0.001f ) ||
				( maxError.z >= 0.001f ) )
			{
				::swprintf_s( temp, sizeof( temp ) >> 1, L"Node[%s] : PolygonError\n{\n", ( pNode->splitOrderCount == 0 )? L"Leaf" : L"Branch" );
				::OutputDebugStringW( temp );

				::swprintf_s( temp, sizeof( temp ) >> 1, L"    AABB = Min(%f, %f, %f) Error(%f, %f, %f)\n", mi.x, mi.y, mi.z, minError.x, minError.y, minError.z );
				::OutputDebugStringW( temp );

				::swprintf_s( temp, sizeof( temp ) >> 1, L"           Max(%f, %f, %f) Error(%f, %f, %f)\n", ma.x, ma.y, ma.z, maxError.x, maxError.y, maxError.z );
				::OutputDebugStringW( temp );

				for( std::vector<Mix::Tool::Win32::Graphics::VERTEX>::const_iterator it = polygon.vertices.begin(); it != polygon.vertices.end(); ++it )
				{
					if( it == polygon.vertices.begin() )
					{
						::swprintf_s( temp, sizeof( temp ) >> 1, L"    Polygon = (%f, %f, %f)\n", ( *it ).pos.x, ( *it ).pos.y,( *it ).pos.z );
					}
					else
					{
						::swprintf_s( temp, sizeof( temp ) >> 1, L"              (%f, %f, %f)\n", ( *it ).pos.x, ( *it ).pos.y,( *it ).pos.z );
					}

					::OutputDebugStringW( temp );
				}

				::OutputDebugStringW( L"}\n" );
			}
		}
	}
#endif

	if( pNode->splitOrderCount == 0 )
	{
		Mix::Tool::Win32::Graphics::MapMesh* pMesh = NULL;
		Mix::Tool::Win32::Graphics::MapMesh* pEditMesh = NULL;

		//メッシュの取得
		if( pNode->meshNumber == -1 )
		{
			//新規
			pNode->meshNumber = static_cast<int>( m_FileMeshList.size() );

			//編集、保存の両方を行う場合のみ作成
			//メッシュリストに NULL が挿入されるが、それはメッシュ番号のカウント用
			if( m_bRenderingOnly == false )
			{
				pMesh = new Mix::Tool::Win32::Graphics::MapMesh( this );
			}

			pNode->pMesh = pMesh;
			m_FileMeshList.push_back( pMesh );
		}
		else
		{
			//既存
			pMesh = m_FileMeshList[pNode->meshNumber];
		}

		//エデイットメッシュの取得
		if( ( pNode->editMeshes.size() == 0 ) ||
			( pNode->editMeshes.back()->GetKey() != viewMeshIndex ) )
		{
			//新規
			pEditMesh = new Mix::Tool::Win32::Graphics::MapMesh( this );

			//メッシュの判別用に仮のキーを渡しておく( 実際のキーは MapViewMesh で設定されます )
			pEditMesh->SetKey( viewMeshIndex );
			m_EditMeshList.push_back( pEditMesh );

			m_ViewMeshList[viewMeshIndex]->AddEditMeshPtr( pEditMesh );

			pNode->editMeshes.push_back( pEditMesh );
		}
		else
		{
			//既存
			pEditMesh = pNode->editMeshes.back();
		}

		//ポリゴンの挿入
		if( polygon.vertices.size() > 3 )
		{
			//三角形ではないのでシザリングを行う

			unsigned int i;
			unsigned int materialNo;
			unsigned int polygonCount;
			const Mix::Tool::Win32::Graphics::VERTEX* pSrcVertex;
			Mix::Tool::Win32::Graphics::POLYGON triPolygon;

			materialNo = polygon.materialNo;
			polygonCount = ( polygon.vertices.size() - 2 );
			pSrcVertex = &( polygon.vertices[0] );
			triPolygon.vertices.resize( 3 );

			if( m_bRenderingOnly == false )
			{
				for( i = 0; i < polygonCount; i++ )
				{
					triPolygon.materialNo = materialNo;
					triPolygon.vertices[0] = ( pSrcVertex[0] );
					triPolygon.vertices[1] = ( pSrcVertex[( i + 1 )] );
					triPolygon.vertices[2] = ( pSrcVertex[( i + 2 )] );

					pMesh->AddPolygon( triPolygon );
					pEditMesh->AddPolygon( triPolygon );
				}
			}
			else
			{
				for( i = 0; i < polygonCount; i++ )
				{
					triPolygon.materialNo = materialNo;
					triPolygon.vertices[0] = ( pSrcVertex[0] );
					triPolygon.vertices[1] = ( pSrcVertex[( i + 1 )] );
					triPolygon.vertices[2] = ( pSrcVertex[( i + 2 )] );

					pEditMesh->AddPolygon( triPolygon );
				}
			}
		}
		else
		{
			//三角形なのでそのまま挿入

			if( m_bRenderingOnly == false )
			{
				pMesh->AddPolygon( polygon );
				pEditMesh->AddPolygon( polygon );
			}
			else
			{
				pEditMesh->AddPolygon( polygon );
			}
		}
	}
	else
	{
		int i;
		unsigned int j;
		unsigned int num;
		const D3DXVECTOR3& aabbMin = pNode->aabb.min;
		const D3DXVECTOR3& aabbMax = pNode->aabb.max;
		D3DXVECTOR3 center = ( ( aabbMin + aabbMax ) * 0.5f );
		D3DXVECTOR3 p0;
		D3DXVECTOR3 p1;
		D3DXVECTOR3 p2;
		Mix::Tool::Win32::Graphics::PLANE plane;
		Mix::Tool::Win32::Graphics::POLYGON frontPolygon;
		Mix::Tool::Win32::Graphics::POLYGON backPolygon;
		Mix::Tool::Win32::Graphics::PolygonDivider polygonDivider;
		std::vector<Mix::Tool::Win32::Graphics::POLYGON> polygonList;

		polygonList.reserve( 8 );
		polygonList.push_back( polygon );

		//分割平面にしたがってポリゴンを分割
		for( i = 0; i < pNode->splitOrderCount; i++ )
		{
			switch( pNode->splitOrder[i] )
			{
			case OctreeModel::OCT_NODE::SO_YZ:
				p0 = D3DXVECTOR3( center.x, aabbMax.y, aabbMin.z );
				p1 = D3DXVECTOR3( center.x, aabbMax.y, aabbMax.z );
				p2 = D3DXVECTOR3( center.x, aabbMin.y, aabbMax.z );
				break;
			case OctreeModel::OCT_NODE::SO_XZ:
				p0 = D3DXVECTOR3( aabbMin.x, center.y, aabbMax.z );
				p1 = D3DXVECTOR3( aabbMax.x, center.y, aabbMax.z );
				p2 = D3DXVECTOR3( aabbMax.x, center.y, aabbMin.z );
				break;
			case OctreeModel::OCT_NODE::SO_XY:
				p0 = D3DXVECTOR3( aabbMax.x, aabbMax.y, center.z );
				p1 = D3DXVECTOR3( aabbMin.x, aabbMax.y, center.z );
				p2 = D3DXVECTOR3( aabbMin.x, aabbMin.y, center.z );
				break;
			}

			Mix::Tool::Win32::Graphics::PLANE::Compute( p0, p1, p2, plane );

			num = polygonList.size();

			for( j = 0; j < num; j++ )
			{
				polygonDivider.Process( polygonList[j], plane, frontPolygon, backPolygon );

				polygonList[j] = backPolygon;
				polygonList.push_back( frontPolygon );
			}
		}

		//子を処理する
		for( j = 0; j < pNode->childs.size(); j++ )
		{
			const Mix::Tool::Win32::Graphics::POLYGON& childPolygon = polygonList[j];

			if( childPolygon.vertices.size() > 0 )
			{
				Octree_InsertPolygon( viewMeshIndex, pNode->childs[j], childPolygon );
			}
		}
	}
}

bool OctreeModel::Octree_Optimize1( Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode )
{
	std::vector<OctreeModel::OCT_NODE*>::iterator it;

	//一旦最下層までいく
	it = pNode->childs.begin();
	while( it != pNode->childs.end() )
	{
		if( Octree_Optimize1( ( *it ) ) == true )
		{
			delete ( *it );
			( *it ) = NULL;
		}

		it++;
	}

	//子のリストを切り詰める
	it = pNode->childs.begin();
	while( it != pNode->childs.end() )
	{
		if( ( *it ) == NULL )
		{
			it = pNode->childs.erase( it );
		}
		else
		{
			it++;
		}
	}

	if( ( pNode->childs.size() == 0 ) &&
		( pNode->meshNumber == -1 ) )
	{
		//メッシュを持たないので削除を要求
		return true;
	}

	return false;
}

void OctreeModel::Octree_Optimize2( Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode )
{
	const std::vector<OctreeModel::OCT_NODE*>& childList = pNode->childs;

	std::vector<OctreeModel::OCT_NODE*>::const_iterator it_c_begin = childList.begin();
	std::vector<OctreeModel::OCT_NODE*>::const_iterator it_c_end = childList.end();
	std::vector<OctreeModel::OCT_NODE*>::const_iterator it_c;

	Mix::Tool::Win32::Geometry::AABB aabb;

	//一旦最下層までいく
	for( it_c = it_c_begin; it_c != it_c_end; ++it_c )
	{
		Octree_Optimize2( ( *it_c ) );
	}

	//AABBを初期化
	aabb.min.x = FLT_MAX;
	aabb.min.y = FLT_MAX;
	aabb.min.z = FLT_MAX;
	aabb.max.x = -FLT_MAX;
	aabb.max.y = -FLT_MAX;
	aabb.max.z = -FLT_MAX;

	//AABBを切り詰める
	if( pNode->meshNumber >= 0 )
	{
		const OctreeModel::MeshList& meshes = pNode->editMeshes;
		OctreeModel::MeshList::const_iterator it_m_begin = meshes.begin();
		OctreeModel::MeshList::const_iterator it_m_end = meshes.end();
		OctreeModel::MeshList::const_iterator it_m;

		for( it_m = it_m_begin; it_m != it_m_end; ++it_m )
		{
			const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& polygons = ( *it_m )->GetPolygonList();
			std::vector<Mix::Tool::Win32::Graphics::POLYGON>::const_iterator it_p_begin = polygons.begin();
			std::vector<Mix::Tool::Win32::Graphics::POLYGON>::const_iterator it_p_end = polygons.end();
			std::vector<Mix::Tool::Win32::Graphics::POLYGON>::const_iterator it_p;

			for( it_p = it_p_begin; it_p != it_p_end; ++it_p )
			{
				const std::vector<Mix::Tool::Win32::Graphics::VERTEX>& vertices = ( *it_p ).vertices;
				std::vector<Mix::Tool::Win32::Graphics::VERTEX>::const_iterator it_v_begin = vertices.begin();
				std::vector<Mix::Tool::Win32::Graphics::VERTEX>::const_iterator it_v_end = vertices.end();
				std::vector<Mix::Tool::Win32::Graphics::VERTEX>::const_iterator it_v;

				for( it_v = it_v_begin; it_v != it_v_end; ++it_v )
				{
					const Mix::Tool::Win32::Graphics::VERTEX& v = ( *it_v );

					if( aabb.min.x > v.pos.x ) { aabb.min.x = v.pos.x; }
					if( aabb.min.y > v.pos.y ) { aabb.min.y = v.pos.y; }
					if( aabb.min.z > v.pos.z ) { aabb.min.z = v.pos.z; }

					if( aabb.max.x < v.pos.x ) { aabb.max.x = v.pos.x; }
					if( aabb.max.y < v.pos.y ) { aabb.max.y = v.pos.y; }
					if( aabb.max.z < v.pos.z ) { aabb.max.z = v.pos.z; }
				}
			}
		}
	}
	else
	{
		for( it_c = it_c_begin; it_c != it_c_end; ++it_c )
		{
			const Mix::Tool::Win32::Geometry::AABB& childAABB = ( *it_c )->aabb;

			if( aabb.min.x > childAABB.min.x ) { aabb.min.x = childAABB.min.x; }
			if( aabb.min.y > childAABB.min.y ) { aabb.min.y = childAABB.min.y; }
			if( aabb.min.z > childAABB.min.z ) { aabb.min.z = childAABB.min.z; }

			if( aabb.max.x < childAABB.max.x ) { aabb.max.x = childAABB.max.x; }
			if( aabb.max.y < childAABB.max.y ) { aabb.max.y = childAABB.max.y; }
			if( aabb.max.z < childAABB.max.z ) { aabb.max.z = childAABB.max.z; }
		}
	}

	pNode->aabb = aabb;
}

void OctreeModel::Octree_Finalize( std::vector<OctreeModel::OCT_NODE*>& nodeList, OctreeModel::OCT_NODE* pNode )
{
	std::vector<OctreeModel::OCT_NODE*>::iterator it;
	std::vector<OctreeModel::OCT_NODE*>::iterator it_begin = pNode->childs.begin();
	std::vector<OctreeModel::OCT_NODE*>::iterator it_end = pNode->childs.end();

	//ノードに番号を割り振り、リストに追加
	pNode->number = static_cast<int>( nodeList.size() );
	nodeList.push_back( pNode );

	//ログ
	LogPrint( LT_INFO, L"    Node(%d) : OptimizeMesh[%s] AABB[MIN(%f,%f,%f) MAX(%f,%f,%f)]",
		pNode->number,
		( pNode->meshNumber >= 0 )? L"○" : L"×",
		pNode->aabb.min.x,
		pNode->aabb.min.y,
		pNode->aabb.min.z,
		pNode->aabb.max.x,
		pNode->aabb.max.y,
		pNode->aabb.max.z );

	for( it = it_begin; it != it_end; ++it )
	{
		if( ( *it ) != NULL )
		{
			Octree_Finalize( nodeList, ( *it ) );
		}
	}
}

void OctreeModel::Octree_Release( OctreeModel::OCT_NODE* pNode )
{
	if( pNode == NULL )
	{
		return;
	}

	for( std::vector<OctreeModel::OCT_NODE*>::iterator it = pNode->childs.begin(); it != pNode->childs.end(); ++it )
	{
		Octree_Release( ( *it ) );
	}

	delete pNode;
}

bool OctreeModel::FBX_LoadNode(	const OctreeModel::IMPORT_CONFIG& cfg,
								OctreeModel::MeshList& meshes,
								Mix::Tool::Win32::Graphics::FBX* pFbx,
								FbxNode* pFbxNode,
								Mix::Tool::Win32::Graphics::Node* pParentNode )
{
	int childCount = pFbxNode->GetChildCount();
	Mix::Tool::Win32::Graphics::Node* pNode = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ノードを追加
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pNode = new Mix::Tool::Win32::Graphics::Node(
		this,
		static_cast<int>( m_NodeList.size() ),
		pFbxNode->GetName(),
		pParentNode,
		pFbx->GetGeometricMatrix( pFbxNode ),
		pFbx->GetLocalScale( pFbxNode ),
		pFbx->GetLocalRotation( pFbxNode ),
		pFbx->GetLocalTranslation( pFbxNode ) );

	if( pNode != NULL )
	{
		m_NodeList.push_back( pNode );
	}
	else
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュの処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( FBX::GetNodeType( pFbxNode ) == FbxNodeAttribute::eMesh )
	{
		std::wstring temp;

		//名前をユニコードに変換
		Mix::Tool::Win32::AnsiToWide( pFbxNode->GetName(), temp );

		//ポリゴンの三角形化
		if( pFbx->TriangulateInPlace( pFbxNode, temp.c_str() ) == true )
		{
			Mix::Tool::Win32::Graphics::MapMesh* pMesh = new Mix::Tool::Win32::Graphics::MapMesh( this );

			if( pMesh != NULL )
			{
				bool bPolygonNormal = ( ( cfg.meshBuildType == Mix::Tool::Win32::Graphics::MESH_BUILD_OPTIMIZE ) && ( cfg.bSmooting == true ) );

				if( pFbx->LoadMesh( pFbxNode, true, bPolygonNormal, pMesh, temp.c_str() ) == true )
				{
					Mix::Tool::Win32::Graphics::MapViewMesh* pViewMesh = new Mix::Tool::Win32::Graphics::MapViewMesh();

					if( pViewMesh != NULL )
					{
						unsigned int polygonCount = pMesh->GetPolygonCount();

						//ポリゴン数を設定
						pViewMesh->SetPolygonCount( polygonCount );

						//マテリアルスロットインデックスを設定
						if( polygonCount > 0 )
						{
							const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& polygonList = pMesh->GetPolygonList();
							const Mix::Tool::Win32::Graphics::POLYGON* pPolygon = &( polygonList[0] );
							const Mix::Tool::Win32::Graphics::POLYGON* pPolygonEnd = pPolygon + polygonCount;

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

							pViewMesh->SortMaterialSlotIndex();
						}

						//ノードをメッシュに設定
						pNode->SetMesh( m_ViewMeshList.size(), pViewMesh );

						//リストに追加
						meshes.push_back( pMesh );
						m_ViewMeshList.push_back( pViewMesh );
					}
					else
					{
						MIX_DELETE( pMesh );
						return false;
					}
				}
				else
				{
					MIX_DELETE( pMesh );
					return false;
				}
			}
			else
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 子ノードを処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( int i = 0; i < childCount; i++ )
	{
		if( FBX_LoadNode( cfg, meshes, pFbx, pFbxNode->GetChild( i ), pNode ) == false )
		{
			return false;
		}
	}

	return true;
}

void OctreeModel::ReleaseMeshes( OctreeModel::MeshList& meshes )
{
	if( meshes.size() > 0 )
	{
		OctreeModel::MeshList::iterator it_begin = meshes.begin();
		OctreeModel::MeshList::iterator it_end = meshes.end();
		OctreeModel::MeshList::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			MapMesh* pMesh = ( *it );
			MIX_DELETE( pMesh );
		}

		meshes.clear();
	}
}

}}}}
