#include "Mix/Private/Graphics/Utility/Common/PerspectiveRenderer.h"

#include "Mix/Graphics/IDevice.h"
#include "Mix/Graphics/IShaderConstant.h"
#include "Mix/Graphics/IVertexShader.h"
#include "Mix/Graphics/IPixelShader.h"
#include "Mix/Graphics/IVertexLayout.h"
#include "Mix/Graphics/IVertexBuffer.h"

#include "Mix/Private/Graphics/Common/Manager.h"

namespace Mix{ namespace Graphics{ namespace Utility{ namespace Common{

const UInt32 PerspectiveRenderer::BOX_VERTEX_NUM = 24;

const UInt32 PerspectiveRenderer::BOX_INDEX_TABLE[24] =
{
	0, 1,
	1, 2,
	2, 3,
	3, 0,

	4, 5,
	5, 6,
	6, 7,
	7, 4,

	0, 4,
	1, 5,
	2, 6,
	3, 7,
};

const UInt32 PerspectiveRenderer::OBB_VERTEX_NUM = 24;

const UInt32 PerspectiveRenderer::OBB_INDEX_TABLE[24] =
{
	0, 1, 1, 2, 2, 3, 3, 0,
	4, 5, 5, 6, 6, 7, 7, 4,
	0, 4, 1, 5, 2, 6, 3, 7,
};

const UInt32 PerspectiveRenderer::AXIS_DIR_NUM = 3;

const Mix::Vector3 PerspectiveRenderer::AXIS_DIR_TABLE[3] =
{
	Mix::Vector4( 1.0f, 0.0f, 0.0f, 1.0f ),
	Mix::Vector4( 0.0f, 1.0f, 0.0f, 1.0f ),
	Mix::Vector4( 0.0f, 0.0f, 1.0f, 1.0f ),
};

const UInt32 PerspectiveRenderer::AABB_VERTEX_NUM = 24;

const UInt32 PerspectiveRenderer::AABB_INDEX_TABLE[24] =
{
	0, 1, 1, 2, 2, 3, 3, 0,
	4, 5, 5, 6, 6, 7, 7, 4,
	0, 4, 1, 5, 2, 6, 3, 7,
};

const UInt32 PerspectiveRenderer::FRUSTUM_POINT_NUM = 8;
const UInt32 PerspectiveRenderer::FRUSTUM_INDEX_NUM = 28;
const Mix::Vector3 PerspectiveRenderer::FRUSTUM_POINT_TABLE[PerspectiveRenderer::FRUSTUM_POINT_NUM] =
{
	Mix::Vector3( -1.0f,  1.0f, -1.0f ),
	Mix::Vector3( -1.0f, -1.0f, -1.0f ),
	Mix::Vector3(  1.0f, -1.0f, -1.0f ),
	Mix::Vector3(  1.0f,  1.0f, -1.0f ),

	Mix::Vector3( -1.0f,  1.0f,  1.0f ),
	Mix::Vector3( -1.0f, -1.0f,  1.0f ),
	Mix::Vector3(  1.0f, -1.0f,  1.0f ),
	Mix::Vector3(  1.0f,  1.0f,  1.0f ),
};

const UInt32 PerspectiveRenderer::FRUSTUM_INDEX_TABLE[PerspectiveRenderer::FRUSTUM_INDEX_NUM] =
{
	0, 1,
	1, 2,
	2, 3,
	3, 0,

	4, 5,
	5, 6,
	6, 7,
	7, 4,

	0, 4,
	1, 5,
	2, 6,
	3, 7,

	4, 6,
	5, 7,
};

PerspectiveRenderer* PerspectiveRenderer::CreateInstance( const wchar_t* pDebugName )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GRAPHICS, PerspectiveRenderer, pDebugName );
}

PerspectiveRenderer* PerspectiveRenderer::CreateInstance( UInt32 defSize, UInt32 resizeStep, const wchar_t* pDebugName )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GRAPHICS, PerspectiveRenderer, defSize, resizeStep, pDebugName );
}

PerspectiveRenderer::PerspectiveRenderer( const wchar_t* pDebugName ):
m_pDevice( NULL ),
m_pShaderConstant( NULL ),
m_pVertexLayout( NULL ),
m_pVertexShader( NULL ),
m_pPixelShader( NULL ),
m_pVertexBuffer( NULL ),
m_Color( 1.0f, 1.0f, 1.0f, 1.0f ),
m_CacheSize( 0 ),
m_TransformVertexEntry( 0 )
{
	MIX_ASSERT( pDebugName != NULL );

#ifdef _DEBUG

	Mix::StringW tempStr;

	tempStr.Sprintf( L"PerspectiveRenderer/%s/VertexTable", pDebugName );
	m_VertexTable.Initialize( PerspectiveRenderer::DEF_VERTEXBUFFER_SIZE, PerspectiveRenderer::VERTEXBUFFER_RESIZESTEP, tempStr.GetConstPtr() );

#else //_DEBUG

	m_VertexTable.Initialize( PerspectiveRenderer::DEF_VERTEXBUFFER_SIZE, PerspectiveRenderer::VERTEXBUFFER_RESIZESTEP );

#endif //_DEBUG
}

PerspectiveRenderer::PerspectiveRenderer( UInt32 defSize, UInt32 resizeStep, const wchar_t* pDebugName ) :
m_pDevice( NULL ),
m_pShaderConstant( NULL ),
m_pVertexLayout( NULL ),
m_pVertexShader( NULL ),
m_pPixelShader( NULL ),
m_pVertexBuffer( NULL ),
m_Color( 1.0f, 1.0f, 1.0f, 1.0f ),
m_CacheSize( 0 ),
m_TransformVertexEntry( 0 )
{
	MIX_ASSERT( pDebugName != NULL );

#ifdef _DEBUG

	Mix::StringW tempStr;

	tempStr.Sprintf( L"PerspectiveRenderer/%s/VertexTable", pDebugName);
	m_VertexTable.Initialize( defSize, resizeStep, tempStr.GetConstPtr() );

#else //_DEBUG

	m_VertexTable.Initialize( PerspectiveRenderer::DEF_VERTEXBUFFER_SIZE, PerspectiveRenderer::VERTEXBUFFER_RESIZESTEP );

#endif //_DEBUG
}

PerspectiveRenderer::~PerspectiveRenderer( void )
{
	MIX_RELEASE( m_pVertexBuffer );
	MIX_RELEASE( m_pPixelShader );
	MIX_RELEASE( m_pVertexShader );
	MIX_RELEASE( m_pVertexLayout );
	MIX_RELEASE( m_pShaderConstant );
	MIX_RELEASE( m_pDevice );
}

Boolean PerspectiveRenderer::Initialize( Mix::Graphics::Common::Manager* pManager, const wchar_t* pDebugName )
{
	MIX_ASSERT( pDebugName != NULL );

	Mix::StringW tempStr;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// デバイスを取得
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	pManager->GetDevice( &m_pDevice );

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// シェーダーを取得
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Graphics::Common::Manager::VERTEX_EFFECT* pVE = pManager->GetVertexEffectPtr( Mix::Graphics::Common::Manager::ER_LINE );
	Mix::Graphics::Common::Manager::PIXEL_EFFECT* pPE = pManager->GetPixelEffectPtr( Mix::Graphics::Common::Manager::ER_LINE );

	MIX_ASSERT( pVE != NULL );
	MIX_ASSERT( pPE != NULL );

	MIX_ADD_REF( pVE->pLayout );
	m_pVertexLayout = pVE->pLayout;

	MIX_ADD_REF( pVE->pShader[Mix::Graphics::Common::Manager::VES_DEFAULT] );
	m_pVertexShader = pVE->pShader[Mix::Graphics::Common::Manager::VES_DEFAULT];

	MIX_ADD_REF( pPE->pShader[Mix::Graphics::Common::Manager::PES_DEFAULT] );
	m_pPixelShader = pPE->pShader[Mix::Graphics::Common::Manager::PES_DEFAULT];

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// シェーダー定数を作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Graphics::SHADER_MODEL shaderModel = m_pDevice->GetShaderModel();

	if( shaderModel == Mix::Graphics::SHADER_MODEL_3 )
	{
		//DirectX9
		m_pShaderConstant = NULL;
	}
	else
	{
		tempStr.Sprintf( L"PerspectiveRenderer/%s/ShaderConstant", pDebugName );

		//DirectX10 or 11
		if( m_pDevice->CreateShaderConstant( sizeof( Float32[4][4] ), True, NULL, &m_pShaderConstant, tempStr.GetConstPtr() ) == False )
		{
			return False;
		}
	}

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

	tempStr.Sprintf( L"PerspectiveRenderer/%s/VertexBuffer", pDebugName );

	if( m_pDevice->CreateVertexBuffer(	PerspectiveRenderer::DEF_VERTEXBUFFER_SIZE, sizeof( PerspectiveRenderer::VERTEX ),
										True, VERTEXBUFFER_RESIZESTEP, NULL, &m_pVertexBuffer, pDebugName ) == False )
	{
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ラインテーブルの作成
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	const int SPHERE_STEPCOUNT = 10;
	const float SPHERE_STEP = MIX_PI * 360.0f / static_cast<float>( SPHERE_STEPCOUNT ) / 180.0f;
	const int SPHERE_STEPCOUNT_H = SPHERE_STEPCOUNT >> 1;
	const float SPHERE_STEP_H = MIX_PI * 180.0f / static_cast<float>( SPHERE_STEPCOUNT_H ) / 180.0f;

	/*
		球
	*/

	{

		Mix::Vector3 ppx( 0.0f, 0.0f, 1.0f );
		Mix::Vector3 ppy( 0.0f, 0.0f, 1.0f );
		Mix::Vector3 ppz( 1.0f, 0.0f, 0.0f );

		for( int i = 0; i < ( SPHERE_STEPCOUNT + 1 ); i++ )
		{
			Mix::Vector3 px( 0.0f, 0.0f, 1.0f );
			Mix::Vector3 py( 0.0f, 0.0f, 1.0f );
			Mix::Vector3 pz( 1.0f, 0.0f, 0.0f );

			Mix::Matrix4x4 mx;
			Mix::Matrix4x4 my;
			Mix::Matrix4x4 mz;

			mx.SetRotationX( SPHERE_STEP * static_cast<float>( i ) );
			my.SetRotationY( SPHERE_STEP * static_cast<float>( i ) );
			mz.SetRotationZ( SPHERE_STEP * static_cast<float>( i ) );

			px = mx * px;
			py = my * py;
			pz = mz * pz;

			m_SphereLineTable.push_back( ppx );
			m_SphereLineTable.push_back( px );

			m_SphereLineTable.push_back( ppy );
			m_SphereLineTable.push_back( py );

			m_SphereLineTable.push_back( ppz );
			m_SphereLineTable.push_back( pz );

			ppx = px;
			ppy = py;
			ppz = pz;
		}
	}
	
	/*
		半球
	*/

	{
		Mix::Vector3 ppx( 0.0f, 0.0f, 1.0f );
		Mix::Vector3 ppz( 1.0f, 0.0f, 0.0f );

		for( int i = 0; i < ( SPHERE_STEPCOUNT_H + 1 ); i++ )
		{
			Mix::Vector3 px( 0.0f, 0.0f, 1.0f );
			Mix::Vector3 pz( 1.0f, 0.0f, 0.0f );

			Mix::Matrix4x4 mx;
			Mix::Matrix4x4 mz;

			mx.SetRotationX( -SPHERE_STEP_H * static_cast<float>( i ) );
			mz.SetRotationZ( SPHERE_STEP_H * static_cast<float>( i ) );

			px = mx * px;
			pz = mz * pz;

			m_HemisphereLineTable.push_back( ppx );
			m_HemisphereLineTable.push_back( px );

			m_HemisphereLineTable.push_back( ppz );
			m_HemisphereLineTable.push_back( pz );

			ppx = px;
			ppz = pz;
		}
	}

	/*
		シリンダー
	*/

	{
		// エッジ
		Mix::Vector3 ppy( 0.0f, 0.0f, 1.0f );

		for( int i = 0; i < ( SPHERE_STEPCOUNT + 1 ); i++ )
		{
			Mix::Vector3 py( 0.0f, 0.0f, 1.0f );

			Mix::Matrix4x4 my;

			my.SetRotationY( SPHERE_STEP * static_cast<float>( i ) );

			py = my * py;

			m_CylinderLineTable.push_back( Mix::Vector3( ppy.x, +1.0f, ppy.z ) );
			m_CylinderLineTable.push_back( Mix::Vector3(  py.x, +1.0f,  py.z ) );

			m_CylinderLineTable.push_back( Mix::Vector3( ppy.x, -1.0f, ppy.z ) );
			m_CylinderLineTable.push_back( Mix::Vector3(  py.x, -1.0f,  py.z ) );

			ppy = py;
		}

		// 表面

		m_CylinderLineTable.push_back( Mix::Vector3( +1.0f, +1.0f, +0.0f ) );
		m_CylinderLineTable.push_back( Mix::Vector3( +1.0f, -1.0f, +0.0f ) );
		m_CylinderLineTable.push_back( Mix::Vector3( -1.0f, +1.0f, +0.0f ) );
		m_CylinderLineTable.push_back( Mix::Vector3( -1.0f, -1.0f, +0.0f ) );

		m_CylinderLineTable.push_back( Mix::Vector3( +0.0f, +1.0f, +1.0f ) );
		m_CylinderLineTable.push_back( Mix::Vector3( +0.0f, -1.0f, +1.0f ) );
		m_CylinderLineTable.push_back( Mix::Vector3( +0.0f, +1.0f, -1.0f ) );
		m_CylinderLineTable.push_back( Mix::Vector3( +0.0f, -1.0f, -1.0f ) );
	}

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

	return True;
}

void PerspectiveRenderer::SetColor( const Mix::Vector4& color )
{
	m_Color = color;
}

const Mix::Vector4& PerspectiveRenderer::GetColor( void ) const
{
	return m_Color;
}

void PerspectiveRenderer::SetMatrix( const Mix::Matrix4x4& mat )
{
	m_Mat = mat;
}

const Mix::Matrix4x4& PerspectiveRenderer::GetMatrix( void ) const
{
	return m_Mat;
}

void PerspectiveRenderer::AddLine( const Mix::Vector3& p0, const Mix::Vector3& p1 )
{
	PerspectiveRenderer::VERTEX* vertices = m_VertexTable.Add( 2 );

	vertices[0].pos = p0;
	vertices[0].color = m_Color;

	vertices[1].pos = p1;
	vertices[1].color = m_Color;

	TransformVertex();
}

void PerspectiveRenderer::AddLines( const Mix::Vector3* pPointTable, UInt32 pointCount )
{
	if( ( pPointTable == NULL ) ||
		( pointCount < 2 ) ||
		( ( pointCount % 2 ) != 0 ) )
	{
		return;
	}

	const Mix::Vector3* pPoint = &( pPointTable[0] );

	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( pointCount );
	PerspectiveRenderer::VERTEX* pVertexEnd = pVertex + pointCount;

	while( pVertex != pVertexEnd )
	{
		pVertex->pos = *pPoint;
		pVertex->color = m_Color;

		pPoint++;
		pVertex++;
	}

	TransformVertex();
}

void PerspectiveRenderer::AddAxis( Float32 scale )
{
	UInt32 start = m_VertexTable.GetCount();
	PerspectiveRenderer::VERTEX* vertices = m_VertexTable.Add( 6 );

	for( UInt32 i = 0; i < 3; i++ )
	{
		Mix::Vector4 dir = PerspectiveRenderer::AXIS_DIR_TABLE[i] * scale;
		Mix::Vector4 color = PerspectiveRenderer::AXIS_DIR_TABLE[i] * m_Color;
		UInt32 offset = i << 1;

		vertices[offset + 0].pos = Mix::Vector4::Zero();
		vertices[offset + 0].color = color;

		vertices[offset + 1].pos = dir;
		vertices[offset + 1].color = color;
	}

	TransformVertex();
}

void PerspectiveRenderer::AddBox( const Mix::Vector3& halfExtents )
{
	Mix::Vector4 points[8] =
	{
		Mix::Vector4( -halfExtents.x, +halfExtents.y, +halfExtents.z, 1.0f ),
		Mix::Vector4( +halfExtents.x, +halfExtents.y, +halfExtents.z, 1.0f ),
		Mix::Vector4( +halfExtents.x, +halfExtents.y, -halfExtents.z, 1.0f ),
		Mix::Vector4( -halfExtents.x, +halfExtents.y, -halfExtents.z, 1.0f ),

		Mix::Vector4( -halfExtents.x, -halfExtents.y, +halfExtents.z, 1.0f ),
		Mix::Vector4( +halfExtents.x, -halfExtents.y, +halfExtents.z, 1.0f ),
		Mix::Vector4( +halfExtents.x, -halfExtents.y, -halfExtents.z, 1.0f ),
		Mix::Vector4( -halfExtents.x, -halfExtents.y, -halfExtents.z, 1.0f ),
	};

	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( 24 );

	for( UInt32 i = 0; i < 24; i++ )
	{
		pVertex->pos = points[PerspectiveRenderer::BOX_INDEX_TABLE[i]];
		pVertex->color = m_Color;

		pVertex++;
	}

	TransformVertex();
}

void PerspectiveRenderer::AddBox( const Mix::Geometry::OBB& obb )
{
	const Mix::Vector3* points = &( obb.points[0] );
	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( PerspectiveRenderer::OBB_VERTEX_NUM );

	for( UInt32 i = 0; i < PerspectiveRenderer::OBB_VERTEX_NUM; i++ )
	{
		const Mix::Vector3& pos = points[PerspectiveRenderer::OBB_INDEX_TABLE[i]];

		pVertex->pos = pos;
		pVertex->color = m_Color;

		pVertex++;
	}

	TransformVertex();
}

void PerspectiveRenderer::AddBox( const Mix::Geometry::AABB& aabb )
{
	const Mix::Vector3* points = &( aabb.points[0] );

	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( PerspectiveRenderer::AABB_VERTEX_NUM );

	for( UInt32 i = 0; i < PerspectiveRenderer::AABB_VERTEX_NUM; i++ )
	{
		const Mix::Vector3& pos = points[PerspectiveRenderer::AABB_INDEX_TABLE[i]];

		pVertex->pos = pos;
		pVertex->color = m_Color;

		pVertex++;
	}

	TransformVertex();
}

void PerspectiveRenderer::AddCylinder( UInt32 axis, Float32 length, Float32 radius )
{
	MIX_ASSERT( axis <= 2 );

	Mix::Matrix4x4 oldMat = GetMatrix();

	Float32 halfPI = MIX_PI * 0.5f;
	Float32 halfLen = length * 0.5f;

	Mix::Matrix4x4 rot;
	Mix::Matrix4x4 temp;

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

	switch( axis )
	{
	case 0:
		rot.SetRotationZ( +halfPI );
		break;
	case 1:
		rot = Mix::Matrix4x4::Identity();
		break;
	case 2:
		rot.SetRotationX( +halfPI );
		break;
	}

	temp.SetScaling( radius, halfLen, radius );
	temp *= rot;

	SetMatrix( temp * oldMat );
	AddLines( &( m_CylinderLineTable[0] ), MIX_UIT_TO_UI32( m_CylinderLineTable.size() ) );

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

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddCapsule( UInt32 axis, Float32 length, Float32 radius )
{
	MIX_ASSERT( axis <= 2 );

	Mix::Matrix4x4 oldMat = GetMatrix();

	Mix::Matrix4x4 rot0;
	Mix::Matrix4x4 rot1;
	Mix::Matrix4x4 tr0;
	Mix::Matrix4x4 tr1;
	Mix::Matrix4x4 temp;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 半球
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Float32 halfPI = MIX_PI * 0.5f;
	Float32 halfLen = max( 0.0f, length - radius * 2.0f ) * 0.5f;

	switch( axis )
	{
	case 0:
		rot0.SetRotationZ( +halfPI );
		rot1.SetRotationZ( -halfPI );
		tr0.SetTranslation( -halfLen, 0.0f, 0.0f );
		tr1.SetTranslation( +halfLen, 0.0f, 0.0f );
		break;

	case 1:
		rot0 = Mix::Matrix4x4::Identity();
		rot1.SetRotationZ( +MIX_PI );
		tr0.SetTranslation( 0.0f, +halfLen, 0.0f );
		tr1.SetTranslation( 0.0f, -halfLen, 0.0f );
		break;

	case 2:
		rot0.SetRotationX( +halfPI );
		rot1.SetRotationX( -halfPI );
		tr0.SetTranslation( 0.0f, 0.0f, +halfLen );
		tr1.SetTranslation( 0.0f, 0.0f, -halfLen );
		break;
	}

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

	temp = Mix::Matrix4x4::Identity();

	temp.SetScaling( radius );
	temp *= rot0;
	temp *= tr0;

	SetMatrix( temp * oldMat );
	AddLines( &( m_HemisphereLineTable[0] ), MIX_UIT_TO_UI32( m_HemisphereLineTable.size() ) );

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

	temp.SetScaling( radius );
	temp *= rot1;
	temp *= tr1;

	SetMatrix( temp * oldMat );
	AddLines( &( m_HemisphereLineTable[0] ), MIX_UIT_TO_UI32( m_HemisphereLineTable.size() ) );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// シリンダー
	////////////////////////////////////////////////////////////////////////////////////////////////////

	switch( axis )
	{
	case 0:
		rot0.SetRotationZ( +halfPI );
		break;
	case 1:
		rot0 = Mix::Matrix4x4::Identity();
		break;
	case 2:
		rot0.SetRotationX( +halfPI );
		break;
	}

	temp.SetScaling( radius, halfLen, radius );
	temp *= rot0;

	SetMatrix( temp * oldMat );
	AddLines( &( m_CylinderLineTable[0] ), MIX_UIT_TO_UI32( m_CylinderLineTable.size() ) );

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

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddFrustum( const Mix::Geometry::Frustum& frustum )
{
	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( PerspectiveRenderer::FRUSTUM_INDEX_NUM );
	Mix::Matrix4x4 invViewProjMat = frustum.GetViewProjectionMatrix().ToInverse();

	for( UInt32 i = 0; i < PerspectiveRenderer::FRUSTUM_INDEX_NUM; i+=2 )
	{
		Mix::Vector3 p0 = invViewProjMat * PerspectiveRenderer::FRUSTUM_POINT_TABLE[PerspectiveRenderer::FRUSTUM_INDEX_TABLE[i]];
		Mix::Vector3 p1 = invViewProjMat * PerspectiveRenderer::FRUSTUM_POINT_TABLE[PerspectiveRenderer::FRUSTUM_INDEX_TABLE[i + 1]];

		pVertex->pos = p0;
		pVertex->color = m_Color;
		pVertex++;

		pVertex->pos = p1;
		pVertex->color = m_Color;
		pVertex++;
	}

	TransformVertex();
}

void PerspectiveRenderer::AddSphere( Float32 radius )
{
	Mix::Matrix4x4 oldMat = GetMatrix();
	Mix::Matrix4x4 tempMat;

	tempMat.SetScaling( radius );

	SetMatrix( tempMat * oldMat );
	AddLines( &( m_SphereLineTable[0] ), MIX_UIT_TO_UI32( m_SphereLineTable.size() ) );

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddSphere( const Mix::Vector3& radius )
{
	Mix::Matrix4x4 oldMat = GetMatrix();
	Mix::Matrix4x4 tempMat;

	tempMat.SetScaling( radius );

	SetMatrix( tempMat * oldMat );
	AddLines( &( m_SphereLineTable[0] ), MIX_UIT_TO_UI32( m_SphereLineTable.size() ) );

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddSphere( const Mix::Vector3& center, Float32 radius )
{
	Mix::Matrix4x4 oldMat = GetMatrix();
	Mix::Matrix4x4 tempMat;

	tempMat.SetScaling( radius );
	tempMat.m30 = center.x;
	tempMat.m31 = center.y;
	tempMat.m32 = center.z;
	tempMat.m33 = 1.0f;

	SetMatrix( tempMat * oldMat );
	AddLines( &( m_SphereLineTable[0] ), MIX_UIT_TO_UI32( m_SphereLineTable.size() ) );

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddSphere( const Mix::Vector3& center, const Mix::Vector3& radius )
{
	Mix::Matrix4x4 oldMat = GetMatrix();
	Mix::Matrix4x4 tempMat;

	tempMat.SetScaling( radius );
	tempMat.m30 = center.x;
	tempMat.m31 = center.y;
	tempMat.m32 = center.z;
	tempMat.m33 = 1.0f;

	SetMatrix( tempMat * oldMat );
	AddLines( &( m_SphereLineTable[0] ), MIX_UIT_TO_UI32( m_SphereLineTable.size() ) );

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddSphere( const Mix::Geometry::Sphere& sphere )
{
	Mix::Matrix4x4 oldMat = GetMatrix();
	Mix::Matrix4x4 tempMat;

	tempMat.SetScaling( sphere.radius );
	tempMat.m30 = sphere.center.x;
	tempMat.m31 = sphere.center.y;
	tempMat.m32 = sphere.center.z;
	tempMat.m33 = 1.0f;

	SetMatrix( tempMat * oldMat );
	AddLines( &( m_SphereLineTable[0] ), MIX_UIT_TO_UI32( m_SphereLineTable.size() ) );

	SetMatrix( oldMat );
}

void PerspectiveRenderer::AddCone( const Mix::Vector3& pos, const Mix::Vector3& vec, Float32 angle, UInt32 divCount )
{
	if( ( MIX_FLOAT_IS_ZERO( vec.GetLength() ) == True ) ||
		( divCount == 0 ) )
	{
		return;
	}

	divCount = MIX_CLAMP( divCount, 4, 36 );

	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( ( 2 + divCount * 2 + divCount * 2 ) );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Vector3 temp = pos + vec;

	pVertex->pos = pos;
	pVertex->color = m_Color;
	pVertex++;

	pVertex->pos = temp;
	pVertex->color = m_Color;
	pVertex++;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 表面
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Vector4 endPos = pos + vec;
	Float32 radius = MIX_2PI * vec.GetLength() * MIX_FLOAT_DIV( angle, MIX_2PI );

	Mix::Vector3 dummy;
	Mix::Vector3 axis;
	Mix::Quaternion rot;
	Mix::Matrix4x4 mat;
	Mix::Vector3 p0;
	Mix::Vector3 p1;

	Mix::PlaneSpace( vec, axis, dummy );

	rot.SetRotationAxis( axis, angle );
	p0 = Mix::Matrix4x4( rot ) * ( axis * radius );

	rot.SetRotationAxis( vec.ToNormalize(), MIX_FLOAT_DIV( MIX_2PI, static_cast<Float32>( divCount ) ) );
	mat.SetRotation( rot );

	p1 = mat * p0;

	for( UInt32 i = 0; i < divCount; i++ )
	{
		Mix::Vector3 w0 = endPos + p0;
		Mix::Vector3 w1 = endPos + p1;

		pVertex->pos = w0;
		pVertex->color = m_Color;
		pVertex++;

		pVertex->pos = w1;
		pVertex->color = m_Color;
		pVertex++;

		pVertex->pos = pos;
		pVertex->color = m_Color;
		pVertex++;

		pVertex->pos = w0;
		pVertex->color = m_Color;
		pVertex++;

		p0 = p1;
		p1 = mat * p0;
	}

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

	TransformVertex();
}

void PerspectiveRenderer::AddDisc( const Mix::Vector3& axis, Float32 radius )
{
	static const Int32 STEP = 30;
	static const Float32 RAD_STEP = MIX_2PI / static_cast<Float32>( STEP );

	Mix::Vector3 basePos;
	Mix::Vector3 dummy;

	Mix::Quaternion rot;
	Mix::Vector3 p0;
	Mix::Vector3 p1;

	Float32 rad = 0.0f;

	Mix::PlaneSpace( axis, basePos, dummy );
	basePos *= radius;

	rot.SetRotationAxis( axis, rad );
	p0 = Mix::Matrix4x4( rot ) * basePos;

	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( STEP * 2 );

	for( Int32 i = 0; i < STEP; i++ )
	{
		rad += RAD_STEP;

		rot.SetRotationAxis( axis, rad );
		p1 = Mix::Matrix4x4( rot ) * basePos;

		pVertex->pos = p0;
		pVertex->color = m_Color;
		pVertex++;

		pVertex->pos = p1;
		pVertex->color = m_Color;
		pVertex++;

		p0 = p1;
	}

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

	TransformVertex();
}

void PerspectiveRenderer::AddPolygon( const Mix::Vector3* points, UInt32 pointNum )
{
	if( pointNum < 3 )
	{
		return;
	}

	PerspectiveRenderer::VERTEX* pVertex = m_VertexTable.Add( pointNum * 2 );
	UInt32 j;

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

	for( UInt32 i = 0; i < pointNum; i++ )
	{
		j = ( i + 1 ) % pointNum;

		pVertex->pos = points[i];
		pVertex->color = m_Color;
		pVertex++;

		pVertex->pos = points[j];
		pVertex->color = m_Color;
		pVertex++;
	}

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

	TransformVertex();
}

void PerspectiveRenderer::Update( void )
{
	UInt32 vertexCount = m_VertexTable.GetCount();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 頂点バッファ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( vertexCount > 0 )
	{
		if( m_pVertexBuffer->Lock() == True )
		{
			m_pVertexBuffer->Push( &( m_VertexTable[0] ), vertexCount );
			m_pVertexBuffer->Unlock();

			m_CacheSize = vertexCount;
		}
	}
	else
	{
		m_pVertexBuffer->Clear();
	}

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

	m_VertexTable.Clear();
	m_TransformVertexEntry = 0;
}

void PerspectiveRenderer::Draw( const Mix::Matrix4x4& viewProjMat )
{
#ifdef _DEBUG
	if( m_VertexTable.GetCount() > 0 )
	{
		MIX_ERROR( L"パースペクティブレンダラー : 更新を行わず、描画を行おうとしました" );
	}
#endif //_DEBUG

	if( m_pVertexBuffer->GetCount() == 0 )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Matrix4x4 tpViewProjMat = viewProjMat.ToTranspose();

	m_pDevice->SetVertexShader( m_pVertexShader );
	m_pDevice->SetPixelShader( m_pPixelShader );
	m_pDevice->SetVertexLayout( m_pVertexLayout );
	m_pDevice->SetVertexBuffer( m_pVertexBuffer );

	if( m_pShaderConstant == NULL )
	{
		//DirectX9
		m_pDevice->SetVertexShaderConstantF( 0, &tpViewProjMat, 1 );
	}
	else
	{
		//DirectX10 or 11
		if( m_pShaderConstant->Lock() == True )
		{
			m_pShaderConstant->Write( &( tpViewProjMat.m[0][0] ), sizeof( Float32[4][4] ) );
			m_pShaderConstant->Unlock();
		}

		m_pDevice->SetVertexShaderConstant( 0, m_pShaderConstant );
	}

	m_pDevice->Draw( Mix::Graphics::PRIMITIVE_LINELIST, 0, m_pVertexBuffer->GetCount() );
}

void PerspectiveRenderer::TransformVertex( void )
{
	MIX_ASSERT( m_TransformVertexEntry < m_VertexTable.GetCount() );

	UInt32 count = m_VertexTable.GetCount();

	PerspectiveRenderer::VERTEX* pVertex = &( m_VertexTable[m_TransformVertexEntry] );
	PerspectiveRenderer::VERTEX* pVertexEnd = ( pVertex + ( count - m_TransformVertexEntry ) );

#ifdef _MIX_USE_SSE2

	Float32 m00 = m_Mat.m00;
	Float32 m01 = m_Mat.m01;
	Float32 m02 = m_Mat.m02;
	Float32 m03 = m_Mat.m03;
	Float32 m10 = m_Mat.m10;
	Float32 m11 = m_Mat.m11;
	Float32 m12 = m_Mat.m12;
	Float32 m13 = m_Mat.m13;
	Float32 m20 = m_Mat.m20;
	Float32 m21 = m_Mat.m21;
	Float32 m22 = m_Mat.m22;
	Float32 m23 = m_Mat.m23;
	Float32 m30 = m_Mat.m30;
	Float32 m31 = m_Mat.m31;
	Float32 m32 = m_Mat.m32;
	Float32 m33 = m_Mat.m33;

	Mix::Vector4* pos;

	_declspec( align( 16 ) ) Float32 data[4];

	__m128 mt;
	__m128 mr;
	__m128 mv;

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

		mr = _mm_setzero_ps();

		mt = _mm_set_ps( m02, m01, m00, m03 );
		mv = _mm_set1_ps( pos->x );
		mr = _mm_mul_ps( mt, mv );

		mt = _mm_set_ps( m12, m11, m10, m13 );
		mv = _mm_set1_ps( pos->y );
		mt = _mm_mul_ps( mt, mv );
		mr = _mm_add_ps( mr, mt );

		mt = _mm_set_ps( m22, m21, m20, m23 );
		mv = _mm_set1_ps( pos->z );
		mt = _mm_mul_ps( mt, mv );
		mr = _mm_add_ps( mr, mt );

		mt = _mm_set_ps( m32, m31, m30, m33 );
		mv = _mm_set1_ps( pos->w );
		mt = _mm_mul_ps( mt, mv );
		mr = _mm_add_ps( mr, mt );

		_mm_store_ps( data, mr );

		pos->x = data[1];
		pos->y = data[2];
		pos->z = data[3];
		pos->w = data[0];

		pVertex++;
	}

#else //_MIX_USE_SSE2

	while( pVertex != pVertexEnd )
	{
		pVertex->pos = m_Mat * pVertex->pos;
		pVertex++;
	}

#endif //_MIX_USE_SSE2

	m_TransformVertexEntry = count;
}

}}}}
