#include "Mix/Private/Scene/Common/LeavingParticle.h"

#include "Mix/Graphics/IDevice.h"
#include "Mix/Graphics/IVertexLayout.h"
#include "Mix/Graphics/IVertexBuffer.h"
#include "Mix/Graphics/IIndexBuffer.h"
#include "Mix/Graphics/Utility/IPerspectiveRenderer.h"

#include "Mix/Scene/ICamera.h"
#include "Mix/Scene/IMaterial.h"
#include "Mix/Scene/IParticleSystem.h"
#include "Mix/Scene/IParticleGenerator.h"
#include "Mix/Scene/IParticleProcessor.h"

#ifdef _DEBUG
	#include "Mix/Private/Scene/Common/Debug.h"
#endif //_DEBUG

namespace Mix{ namespace Scene{ namespace Common{

const wchar_t* LeavingParticle::FAILED_CREATE = L"リービングパーティクルの作成に失敗";
const wchar_t* LeavingParticle::FAILED_SET_MATERIAL = L"リービングパーティクルのマテリアルの設定に失敗";

LeavingParticle* LeavingParticle::CreateInstance(	Mix::Scene::IMaterial* pMaterial, Mix::Scene::IParticleSystem* pSystem, const wchar_t* pDebugName )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, LeavingParticle, pMaterial, pSystem, pDebugName );
}

LeavingParticle::LeavingParticle(	Mix::Scene::IMaterial* pMaterial, Mix::Scene::IParticleSystem* pSystem, const wchar_t* pDebugName ) :
InternalParticle( pDebugName ),
m_pSystem( NULL ),
m_pGenerator( NULL ),
m_pProcessor( NULL ),
m_Status( LeavingParticle::STOP ),
m_bGenLoop( False ),
m_GenTime( 0.0 ),
m_GenTimeCounter( 0.0f ),
m_FarRatio( 0.0f ),
m_RevisionCounter( 0 ),
m_bLocalLighting( False ),
m_bDraw( True ),
m_DefWCResult( Mix::Scene::WCR_FRONT ),
m_pOctObj( NULL )
{
	MIX_ASSERT( pMaterial != NULL );
	MIX_ASSERT( pSystem != NULL );

	UInt32 sysBaseFlags;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// システム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_ADD_REF( pSystem );
	m_pSystem = pSystem;

	m_pSystem->Internal_GetGenerator( &m_pGenerator );

	sysBaseFlags = m_pSystem->GetConfig().baseFlags;
	m_bSimWorldSpace = ( MIX_TESTBIT( sysBaseFlags, IParticleSystem::SIMULATION_WORLD_SPACE ) == IParticleSystem::SIMULATION_WORLD_SPACE );
	m_bBillboard = ( MIX_TESTBIT( sysBaseFlags, IParticleSystem::BILLBOARD ) == IParticleSystem::BILLBOARD );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// サブセット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_Subset.priority = 0;
	m_Subset.pMaterial = NULL;
	m_Subset.quadCount = 0;
	m_Subset.quads = NULL;
	m_Subset.bounds.min = Mix::Vector3::Zero();
	m_Subset.bounds.max = Mix::Vector3::Zero();
	m_Subset.bounds.ComputePoints();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステート
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_State.quadCount = 0;
	m_State.quadCapacity = 0;
	m_State.bSleep = False;
	m_State.farRatio = 0.0f;
	m_State.deltaTimer = 0.0f;
	m_State.skipFrames = 0;
	m_State.curMaxSkipFrames = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デバッグ
	////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG

	Mix::StringW tempStr;

	tempStr.Sprintf( L"%s/QuadList", pDebugName );
	m_QuadPool.Initialize( 16, 4, tempStr.GetConstPtr() );
	m_QuadPool.Add( 16 );

	m_DebugName = pDebugName;
	m_DebugDrawAxisScaling = 0.1f;

#else //_DEBUG

	m_QuadPool.Initialize( 16, 4 );
	m_QuadPool.Add( 16 );

#endif //_DEBUG

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアル
	////////////////////////////////////////////////////////////////////////////////////////////////////

	SetMaterial( pMaterial );
}

LeavingParticle::~LeavingParticle( void )
{
	MIX_ASSERT( m_pOctObj == NULL );

	MIX_RELEASE( m_pProcessor );
	MIX_RELEASE( m_pGenerator );
	MIX_RELEASE( m_pSystem );
}

Boolean LeavingParticle::Initialize( void )
{
	MIX_ASSERT( m_pSystem != NULL );
	MIX_ASSERT( m_pProcessor == NULL );

	return m_pSystem->Internal_CreateProcessor( &m_pProcessor );
}

void LeavingParticle::Attach( Mix::Scene::Common::LeavingParticleObject* pOctObj )
{
	MIX_ASSERT( pOctObj != NULL );
	MIX_ASSERT( m_pOctObj == NULL );

	m_pOctObj = pOctObj;

	RendererObject::SetRendering( True );
}

void LeavingParticle::Detach( void )
{
	if( m_pOctObj != NULL )
	{
		m_pOctObj->Destroy();
		m_pOctObj = NULL;
	}

	RendererObject::SetRendering( False );
}

Boolean LeavingParticle::InternalRefresh( const Mix::Vector3& eyePos, const Mix::Matrix4x4& bbMat )
{
	MIX_ASSERT( m_pProcessor != NULL );

	UInt32 faceNum = m_pProcessor->Internal_GetFaceNum();
	UInt32 quadNum = m_QuadPool.GetCount();

	if( ( faceNum == 0 ) ||
		( quadNum < faceNum ) )
	{
		return False;
	}

	const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();
	Float32 farPos = ( m_WorldPos - eyePos ).GetLengthF() - sysCfg.farMinDist;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 遠さ
	//  マルチビュー対応のため、ステートは直接変更せず、遠くないものを優先する
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( farPos >= 0.0f )
	{
		Float32 temp = MIX_FLOAT_DIV( farPos, ( sysCfg.farMaxDist - sysCfg.farMinDist ) );

		m_FarRatio = min( m_FarRatio, temp );
	}
	else
	{
		m_FarRatio = 0.0f;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Quad - ビルボード
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_bBillboard == True ) && ( faceNum > 0 ) && ( m_State.skipFrames == 0 ) )
	{
		InternalParticle::FixedBillboardSubset( bbMat, m_Subset.quads, m_Subset.quadCount );
	}

	return True;
}

Mix::Scene::Common::WIDGET_AABB_SUBSET* LeavingParticle::GetSubsetPtr( void )
{
	return &m_Subset;
}

#ifdef _DEBUG

UInt32 LeavingParticle::Debug_GetVertexStride( void ) const
{
	MIX_ASSERT( m_pMaterial != NULL );

	return m_pMaterial->GetVertexStride( IMaterial::TR_SIMPLE );
}

void LeavingParticle::Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, UInt32 flags )
{
	Mix::Matrix4x4 oldMat = pPerspectiveRenderer->GetMatrix();
	Mix::Vector4 oldColor = pPerspectiveRenderer->GetColor();

	if( MIX_TESTBIT( flags, Mix::Scene::DDF_PARTICLE_AXIS ) == Mix::Scene::DDF_PARTICLE_AXIS )
	{
		pPerspectiveRenderer->SetMatrix( m_WorldMat );
		pPerspectiveRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
		pPerspectiveRenderer->AddAxis( Debug_GetDrawAxisScaling() );
	}

	if( MIX_TESTBIT( flags, Mix::Scene::DDF_PARTICLE_BOUNDS ) == Mix::Scene::DDF_PARTICLE_BOUNDS )
	{
		pPerspectiveRenderer->SetMatrix( Mix::Matrix4x4::Identity() );
		pPerspectiveRenderer->SetColor( Mix::Scene::Common::Debug::GetDrawColor( Mix::Scene::DDC_BOUNDS ) );
		pPerspectiveRenderer->AddBox( m_Subset.bounds );
	}

	if( MIX_TESTBIT( flags, Mix::Scene::DDF_PARTICLE_GENERATOR ) == Mix::Scene::DDF_PARTICLE_GENERATOR )
	{
		pPerspectiveRenderer->SetMatrix( Mix::Matrix4x4::Identity() );
		pPerspectiveRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
		m_pGenerator->Debug_Draw( m_WorldMat, pPerspectiveRenderer );
	}

	if( MIX_TESTBIT( flags, Mix::Scene::DDF_PARTICLE_PROCESSOR ) == Mix::Scene::DDF_PARTICLE_PROCESSOR )
	{
		UInt32 faceNum = m_pProcessor->Internal_GetFaceNum();
		UInt32 quadCapacity = m_QuadPool.GetCount();

		pPerspectiveRenderer->SetMatrix( Mix::Matrix4x4::Identity() );

		if( ( faceNum > 0 ) && ( quadCapacity >= faceNum ) )
		{
			Mix::Scene::Common::WIDGET_QUAD** ppQuad = m_QuadPool.GetBeginPtr();
			Mix::Scene::Common::WIDGET_QUAD** ppQuadEnd = ppQuad + faceNum;
			Mix::Scene::Common::WIDGET_QUAD* pQuad;

			while( ppQuad != ppQuadEnd )
			{
				pQuad = ( *ppQuad );

				pPerspectiveRenderer->SetColor( pQuad->color );
				pPerspectiveRenderer->AddPolygon( pQuad->points, 4 );

				ppQuad++;
			}
		}

		pPerspectiveRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );

		m_pProcessor->Debug_Draw( m_WorldMat, pPerspectiveRenderer );
	}

	pPerspectiveRenderer->SetColor( oldColor );
	pPerspectiveRenderer->SetMatrix( oldMat );
}

#endif //_DEBUG

void LeavingParticle::SetWorldMatrix( const Mix::Matrix4x4& worldMat )
{
	const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();

	m_WorldMat = worldMat;

	if( m_bSimWorldSpace == True )
	{
		m_WorldPos = m_WorldMat.GetRow( 3 );
	}
	else
	{
		Mix::Geometry::AABB* pBounds = &( m_Subset.bounds );
		Mix::Vector3 minOffset = pBounds->min - m_WorldPos;
		Mix::Vector3 maxOffset = pBounds->max - m_WorldPos;

		m_WorldPos = m_WorldMat.GetRow( 3 );

		pBounds->min = m_WorldPos + minOffset;
		pBounds->max = m_WorldPos + maxOffset;
	}

	if( m_Status != LeavingParticle::STOP )
	{
		m_RevisionCounter++;
	}
}

const Mix::Matrix4x4& LeavingParticle::GetWorldMatrix( void ) const
{
	return m_WorldMat;
}

Boolean LeavingParticle::IsPlay( void ) const
{
	return ( m_Status != LeavingParticle::STOP );
}

void LeavingParticle::Play( Boolean bLoop )
{
	if( bLoop == True )
	{
		if( m_bGenLoop == False )
		{
			m_bGenLoop = True;
			m_GenTime = m_pSystem->Internal_GetGenerateInterval();
			m_GenTimeCounter = 0.0f;

			m_Status = LeavingParticle::REQ_PLAY;
		}
	}
	else
	{
		UInt32 genCount = m_pSystem->Internal_GetGenerateCount();

		if( genCount > 0 )
		{
			const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();

			if( m_bSimWorldSpace == True )
			{
				m_pProcessor->Internal_Add( m_WorldMat, m_pGenerator, genCount );
			}
			else
			{
				m_pProcessor->Internal_Add( m_pGenerator, genCount );
			}

			m_bGenLoop = False;
			m_GenTime = 0.0f;
			m_GenTimeCounter = 0.0f;

			m_Status = LeavingParticle::REQ_STOP;

			m_RevisionCounter++;
		}
	}
}

void LeavingParticle::Stop( Boolean bForce )
{
	MIX_ASSERT( m_pProcessor != NULL );

	m_bGenLoop = False;

	if( bForce == True )
	{
		Mix::Geometry::AABB* pBounds = &( m_Subset.bounds );
		pBounds->min = m_WorldPos;
		pBounds->max = m_WorldPos;
		pBounds->ComputePoints();

		m_pProcessor->Internal_Clear();

		m_Status = LeavingParticle::STOP;
	}
	else
	{
		m_Status = LeavingParticle::REQ_STOP;
	}
}

void LeavingParticle::Play( Boolean bLoop, Float32 dt, UInt32 stepCount )
{
	const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();
	Mix::Geometry::AABB* pBounds = &( m_Subset.bounds );

	UInt32 quadNum;
	UInt32 faceNum;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クリア
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pProcessor->Internal_Clear();

	m_bGenLoop = bLoop;
	m_GenTime = ( bLoop == True )? m_pSystem->Internal_GetGenerateInterval() : 0.0f;
	m_GenTimeCounter = 0.0f;

	m_Status = ( bLoop == True )? LeavingParticle::REQ_PLAY : LeavingParticle::REQ_STOP;

	pBounds->min = m_WorldPos;
	pBounds->max = m_WorldPos;
	pBounds->ComputePoints();

	m_State.quadCount = 0;
	m_State.quadCapacity = 0;
	m_State.bSleep = False;
	m_State.farRatio = 0.0f;
	m_State.deltaTimer = 0.0f;
	m_State.skipFrames = 0;
	m_State.curMaxSkipFrames = 0;

	m_FarRatio = 1.0f;

	m_RevisionCounter = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 時間を進める
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( MIX_FLOAT_IS_ZERO( dt ) == False ) && ( stepCount > 0 ) )
	{
		UInt32 genCount;
		UInt32 i;

		if( m_bGenLoop == True )
		{
			/*
				ループ再生
			*/

			m_GenTime = m_pSystem->Internal_GetGenerateInterval();

			for( i = 0; i < stepCount; i++ )
			{
				if( m_GenTime <= m_GenTimeCounter )
				{
					genCount = m_pSystem->Internal_GetGenerateCount();

					if( genCount > 0 )
					{
						if( m_bSimWorldSpace == True )
						{
							m_pProcessor->Internal_Add( m_WorldMat, m_pGenerator, genCount );
						}
						else
						{
							m_pProcessor->Internal_Add( m_pGenerator, genCount );
						}
					}

					m_GenTime = m_pSystem->Internal_GetGenerateInterval();
					m_GenTimeCounter = 0.0f;
				}

				m_GenTimeCounter += dt;

				m_pProcessor->Internal_Update( dt );
			}
		}
		else
		{
			/*
				一回だけ再生
			*/

			genCount = m_pSystem->Internal_GetGenerateCount();

			if( genCount > 0 )
			{
				if( m_bSimWorldSpace == True )
				{
					m_pProcessor->Internal_Add( m_WorldMat, m_pGenerator, genCount );
				}
				else
				{
					m_pProcessor->Internal_Add( m_pGenerator, genCount );
				}
			}

			for( i = 0; i < stepCount; i++ )
			{
				m_pProcessor->Internal_Update( dt );
			}
		}

		// ステータスの更新( ワンショットのみ )
		if( ( m_Status == LeavingParticle::REQ_STOP ) && ( m_pProcessor->Internal_GetFaceNum() == 0 ) )
		{
			m_pProcessor->Internal_Clear();
			m_Status = LeavingParticle::STOP;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クワッドリストの作成 and 境界を求める
	////////////////////////////////////////////////////////////////////////////////////////////////////

	quadNum = m_QuadPool.GetCount();
	faceNum = m_pProcessor->Internal_GetFaceNum();

	if( quadNum < faceNum )
	{
		ExtendQuadsPool( faceNum - quadNum );
	}

	MIX_ASSERT( m_QuadPool.GetCount() >= faceNum );

	if( faceNum > 0 )
	{
		if( m_bBillboard == True )
		{
			RefreshBillboardSubset( m_bSimWorldSpace, m_WorldMat, m_pProcessor->Internal_GetFaces(), faceNum, 0, m_Subset );
		}
		else
		{
			RefreshDefaultSubset( m_bSimWorldSpace, m_WorldMat, m_pProcessor->Internal_GetFaces(), faceNum, 0, m_Subset );
		}
	}
	else
	{
		m_Subset.bounds.min = m_WorldPos;
		m_Subset.bounds.max = m_WorldPos;
	}

	m_Subset.bounds.ComputePoints();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// オクトリーオブジェクトをリフレッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pOctObj != NULL )
	{
		m_pOctObj->Refresh();
	}
}

const Mix::Scene::ILeavingParticle::STATE& LeavingParticle::GetState( void ) const
{
	return m_State;
}

const Mix::Geometry::AABB& LeavingParticle::GetBounds( void ) const
{
	return m_Subset.bounds;
}

Boolean LeavingParticle::IsVisible( void ) const
{
	return ( m_pOctObj != NULL )? m_pOctObj->IsVisible() : False;
}

Boolean LeavingParticle::IsVisible( UInt32 id ) const
{
	return ( m_pOctObj != NULL )? m_pOctObj->IsVisible( id ) : False;
}

Boolean LeavingParticle::IsIgnored( void ) const
{
	return ( m_pOctObj != NULL )? m_pOctObj->IsIllegal() : True;
}

Boolean LeavingParticle::Clone( Mix::Scene::ILeavingParticle** ppParticle, UInt32 flags )
{
	if( ppParticle == NULL )
	{
		return False;
	}

	const Mix::Scene::IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();
	Boolean bSharedMaterial = ( MIX_TESTBIT( flags, ILeavingParticle::C_SHARED_MATERIAL ) == ILeavingParticle::C_SHARED_MATERIAL );
	Boolean bDuplicateProcessor = ( MIX_TESTBIT( flags, ILeavingParticle::C_DUPLICATE_PROCESSOR ) == ILeavingParticle::C_DUPLICATE_PROCESSOR );

	Mix::Scene::IMaterial* pMaterial = NULL;
	Mix::Scene::Common::LeavingParticle* pParticle = NULL;
	UInt32 quadCapacity = m_QuadPool.GetCount();

#ifdef _DEBUG
	const wchar_t* pDebugName = m_DebugName.GetConstPtr();
#else //_DEBUG
	const wchar_t* pDebugName = Mix::STR_UNKNOWN;
#endif //_DEBUG

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ : 開始
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_LOG_INFO_SECT_START( L"リービングパーティクルの複製 : DebugName[%s]", pDebugName );

	MIX_LOG_INFO( L"[%s] SHARED_MATERIAL", MIX_LOG_BOOLEAN( bSharedMaterial ) );
	MIX_LOG_INFO( L"[%s] DUPLICATE_PROCESSOR", MIX_LOG_BOOLEAN( bDuplicateProcessor ) );
	MIX_LOG_INFO( L"" );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアルの共有、または複製
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( bSharedMaterial == True )
	{
		pMaterial = m_pMaterial;
	}
	else
	{
		if( m_pMaterial->Clone( &pMaterial ) == False )
		{
			MIX_LOG_ERROR( L"マテリアルの複製中にエラーが発生 : Name[%s]", pDebugName );
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pParticle = Mix::Scene::Common::LeavingParticle::CreateInstance( pMaterial, m_pSystem, pDebugName );
	if( pParticle == NULL )
	{
		MIX_LOG_ERROR( L"%s : Name[%s]", Mix::STR_OUTOFMEMORY, pDebugName );

		if( bSharedMaterial == False )
		{
			MIX_RELEASE( pMaterial );
		}

		return False;
	}

	if( bSharedMaterial == False )
	{
		MIX_RELEASE( pMaterial );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パラメータの設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	// Mix::Scene::IEntity

	pParticle->SetDraw( IsDraw() );
	pParticle->SetLocalLighting( IsLocalLighting() );
	pParticle->SetTransparencyPriority( GetTransparencyPriority() );
	pParticle->SetDefaultWaterContainsResult( GetDefaultWaterContainsResult() );

	// Mix::Scene::ILeavingParticle

	pParticle->m_Status = m_Status;

	pParticle->m_bGenLoop = m_bGenLoop;
	pParticle->m_GenTime = m_GenTime;
	pParticle->m_GenTimeCounter = m_GenTimeCounter;

	pParticle->m_RevisionCounter = 1;

	pParticle->m_WorldMat = m_WorldMat;
	pParticle->m_WorldPos = m_WorldPos;

	if( quadCapacity > 0 )
	{
		pParticle->ExtendQuadsPool( quadCapacity );
	}

	pParticle->m_Subset.bounds = m_Subset.bounds;

	// Mix::Scene::ILeavingParticle プロセッサー

	if( ( m_bSimWorldSpace == False ) && ( bDuplicateProcessor == True ) )
	{
		MIX_ASSERT( pParticle->m_pProcessor == NULL );

		if( m_pProcessor->Internal_Duplicate( &( pParticle->m_pProcessor ), pDebugName ) == False )
		{
			MIX_LOG_ERROR( L"%s : 初期化中にエラーが発生( プロセッサーの複製 ) : Name[%s]", pDebugName );
			MIX_RELEASE( pParticle );
			return False;
		}
	}
	else
	{
		if( pParticle->Initialize() == False )
		{
			MIX_LOG_ERROR( L"%s : 初期化中にエラーが発生( プロセッサーの作成 ) : Name[%s]", pDebugName );
			MIX_RELEASE( pParticle );
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 譲渡
	////////////////////////////////////////////////////////////////////////////////////////////////////

	( *ppParticle ) = pParticle;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ : 終了
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_LOG_INFO_SECT_END();

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

	return True;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IParticle
////////////////////////////////////////////////////////////////////////////////////////////////////

void LeavingParticle::Update( Float32 dt )
{
	const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();
	Float32 curDt;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 遠さを更新
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_State.farRatio = m_FarRatio;
	m_FarRatio = 1.0f;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// システムの設定による処理
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	// スリープ
	//  リビジョンカウンタが 0 ではないということは ワールド行列が更新されているため、スリープは行わない

	if( ( m_RevisionCounter == 0 ) &&
		( m_State.bSleep == True ) )
	{
		return;
	}

	// フレームのスキップ
	//  リビジョンカウンタが 0 ではないということは ワールド行列が更新されているため、
	//  スキップしたフレームをリセットして強制的に更新させる。
	//  こうしておかないと、視界から外れている状態から視界に入った際に、以前の位置になっているので、ちらっと見えてします。
	//  m_State.skipFrames = 0 にすることで InternalRefresh でビルボードの更新も行わせる。

	if( ( m_RevisionCounter == 0 ) &&
		( MIX_TESTBIT( sysCfg.behaviorFlags, IParticleSystem::FAR_SKIP_FRAMES ) == IParticleSystem::FAR_SKIP_FRAMES ) )
	{
		m_State.deltaTimer += dt;
		m_State.skipFrames++;
		m_State.curMaxSkipFrames = static_cast<UInt32>( static_cast<Float32>( sysCfg.maxSkipFrames ) * m_State.farRatio );

		curDt = m_State.deltaTimer;

		if( m_State.skipFrames > m_State.curMaxSkipFrames )
		{
			m_State.deltaTimer = 0.0f;
			m_State.skipFrames = 0;
		}
	}
	else
	{
		m_State.deltaTimer = 0.0f;
		m_State.skipFrames = 0;
		m_State.curMaxSkipFrames = 0;

		curDt = dt;
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// プロセッサーの処理
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_State.skipFrames == 0 )
	{
		UInt32 preFaceNum = m_pProcessor->Internal_GetFaceNum();
		UInt32 curFaceNum;

		// 発生

		if( m_bGenLoop == True )
		{
			if( m_GenTime <= m_GenTimeCounter )
			{
				UInt32 genCount = m_pSystem->Internal_GetGenerateCount();

				if( genCount > 0 )
				{
					if( m_bSimWorldSpace == True )
					{
						m_pProcessor->Internal_Add( m_WorldMat, m_pGenerator, genCount );
					}
					else
					{
						m_pProcessor->Internal_Add( m_pGenerator, genCount );
					}

					m_RevisionCounter++;
				}

				m_GenTime = m_pSystem->Internal_GetGenerateInterval();
				m_GenTimeCounter = 0.0f;
			}

			m_GenTimeCounter += dt;
		}

		// 更新

		m_pProcessor->Internal_Update( curDt );

		// 必要に応じてリビジョンカウンタを回す

		curFaceNum = m_pProcessor->Internal_GetFaceNum();

		if( ( preFaceNum != curFaceNum ) || ( curFaceNum > 0 ) )
		{
			m_RevisionCounter++;
		}
	}
	else
	{
		//フレームをスキップした際は発生のタイマーだけ進める

		if( m_bGenLoop == True )
		{
			m_GenTimeCounter += dt;
		}
	}
}

void LeavingParticle::Refresh( void )
{
	const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();
	UInt32 faceNum = m_pProcessor->Internal_GetFaceNum();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クワッド and 境界のリフレッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_RevisionCounter > 0 )
	{
		UInt32 quadNum = m_QuadPool.GetCount();

		// 必要に応じてクワッドリストを拡張

		if( quadNum < faceNum )
		{
			ExtendQuadsPool( faceNum - quadNum );
		}

		// クワッド and 境界のリフレッシュ

		MIX_ASSERT( m_QuadPool.GetCount() >= faceNum );

		if( faceNum > 0 )
		{
			if( m_bSimWorldSpace == True )
			{
				//単独で動く場合は毎フレーム境界をリセット
				m_Subset.bounds.min = m_WorldPos;
				m_Subset.bounds.max = m_WorldPos;
			}

			if( m_bBillboard == True )
			{
				RefreshBillboardSubset( m_bSimWorldSpace, m_WorldMat, m_pProcessor->Internal_GetFaces(), faceNum, 0, m_Subset );
			}
			else
			{
				RefreshDefaultSubset( m_bSimWorldSpace, m_WorldMat, m_pProcessor->Internal_GetFaces(), faceNum, 0, m_Subset );
			}
		}
		else
		{
			m_Subset.bounds.min = m_WorldPos;
			m_Subset.bounds.max = m_WorldPos;
		}

		m_Subset.bounds.ComputePoints();

		// オクトリーオブジェクトをリフレッシュ

		if( m_pOctObj != NULL )
		{
			m_pOctObj->Refresh();
		}

		// ステートを更新
		m_State.quadCount = faceNum;
		m_State.quadCapacity = m_QuadPool.GetCount();

		// リビジョンをクリア

		m_RevisionCounter = 0;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステータスによる処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	switch( m_Status )
	{
	//開始のリクエスト
	case LeavingParticle::REQ_PLAY:
		if( faceNum > 0 )
		{
			m_Status = LeavingParticle::PLAY;
		}
		break;

	//停止のリクエスト
	case LeavingParticle::REQ_STOP:
		if( faceNum == 0 )
		{
			m_pProcessor->Internal_Clear();
			m_Status = LeavingParticle::STOP;
		}
		break;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// スリープ判定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( sysCfg.behaviorFlags, IParticleSystem::INVISIBLE_SLEEP ) == IParticleSystem::INVISIBLE_SLEEP )
	{
		if( ( m_Status == LeavingParticle::REQ_PLAY ) || ( m_Status == LeavingParticle::REQ_STOP ) )
		{
			//再生、または停止のリクエストがでている最中はスリープは行わない
			m_State.bSleep = False;
		}
		else
		{
			m_State.bSleep = ( m_pOctObj != NULL )? ( m_pOctObj->IsVisible() == False ) : True;
		}
	}
	else
	{
		m_State.bSleep = False;
	}
}

void LeavingParticle::Refresh1( void )
{
	UInt32 faceNum = m_pProcessor->Internal_GetFaceNum();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クワッド and 境界のリフレッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_RevisionCounter > 0 )
	{
		UInt32 quadNum = m_QuadPool.GetCount();

		// 必要に応じてクワッドリストを拡張

		if( quadNum < faceNum )
		{
			ExtendQuadsPool( faceNum - quadNum );
		}

		// クワッド and 境界のリフレッシュ

		MIX_ASSERT( m_QuadPool.GetCount() >= faceNum );

		if( faceNum > 0 )
		{
			if( m_bSimWorldSpace == True )
			{
				//単独で動く場合は毎フレーム境界をリセット
				m_Subset.bounds.min = m_WorldPos;
				m_Subset.bounds.max = m_WorldPos;
			}

			if( m_bBillboard == True )
			{
				RefreshBillboardSubset( m_bSimWorldSpace, m_WorldMat, m_pProcessor->Internal_GetFaces(), faceNum, 0, m_Subset );
			}
			else
			{
				RefreshDefaultSubset( m_bSimWorldSpace, m_WorldMat, m_pProcessor->Internal_GetFaces(), faceNum, 0, m_Subset );
			}
		}
		else
		{
			m_Subset.bounds.min = m_WorldPos;
			m_Subset.bounds.max = m_WorldPos;
		}

		m_Subset.bounds.ComputePoints();

		// ステートを更新
		m_State.quadCount = faceNum;
		m_State.quadCapacity = m_QuadPool.GetCount();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステータスによる処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	switch( m_Status )
	{
	//開始のリクエスト
	case LeavingParticle::REQ_PLAY:
		if( faceNum > 0 )
		{
			m_Status = LeavingParticle::PLAY;
		}
		break;

	//停止のリクエスト
	case LeavingParticle::REQ_STOP:
		if( faceNum == 0 )
		{
			m_pProcessor->Internal_Clear();
			m_Status = LeavingParticle::STOP;
		}
		break;
	}
}

void LeavingParticle::Refresh2( void )
{
	const IParticleSystem::CONFIG& sysCfg = m_pSystem->GetConfig();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 八分木に再配置
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_RevisionCounter > 0 )
	{
		if( m_pOctObj != NULL )
		{
			m_pOctObj->Refresh();
		}

		m_RevisionCounter = 0;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// スリープ判定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( sysCfg.behaviorFlags, IParticleSystem::INVISIBLE_SLEEP ) == IParticleSystem::INVISIBLE_SLEEP )
	{
		if( ( m_Status == LeavingParticle::REQ_PLAY ) || ( m_Status == LeavingParticle::REQ_STOP ) )
		{
			//再生、または停止のリクエストがでている最中はスリープは行わない
			m_State.bSleep = False;
		}
		else
		{
			m_State.bSleep = ( m_pOctObj != NULL )? ( m_pOctObj->IsVisible() == False ) : True;
		}
	}
	else
	{
		m_State.bSleep = False;
	}
}

Float32 LeavingParticle::Debug_GetDrawAxisScaling( void ) const
{
#ifdef _DEBUG
	return m_DebugDrawAxisScaling;
#else //_DEBUG
	return 0.0f;
#endif //_DEBUG
}

void LeavingParticle::Debug_SetDrawAxisScaling( Float32 scaling )
{
#ifdef _DEBUG
	m_DebugDrawAxisScaling = max( 0.0f, scaling );
#endif //_DEBUG
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IWidget
////////////////////////////////////////////////////////////////////////////////////////////////////

void LeavingParticle::GetMaterial( Mix::Scene::IMaterial** ppMaterial )
{
	InternalParticle::GetMaterial( ppMaterial );
}

Boolean LeavingParticle::SetMaterial( Mix::Scene::IMaterial* pMaterial )
{
	if( pMaterial == NULL )
	{
		return False;
	}

#ifdef _DEBUG
	if( InternalParticle::CheckMaterial( pMaterial, LeavingParticle::FAILED_SET_MATERIAL, m_DebugName.GetConstPtr() ) == False )
	{
		return False;
	}
#else //_DEBUG
	if( InternalParticle::CheckMaterial( pMaterial, LeavingParticle::FAILED_SET_MATERIAL, Mix::STR_UNKNOWN ) == False )
	{
		return False;
	}
#endif //_DEBUG

	if( InternalParticle::SetMaterial( pMaterial ) == False )
	{
		return False;
	}

	m_Subset.pMaterial = m_pMaterial;

	return True;
}

Boolean LeavingParticle::CanLocalLighting( void ) const
{
	MIX_ASSERT( m_pMaterial != NULL );

	return ( ( m_pMaterial->IsLighting() == True ) && ( m_bLocalLighting == True ) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IEntity
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean LeavingParticle::IsDraw( void ) const
{
	return m_bDraw;
}

void LeavingParticle::SetDraw( Boolean state )
{
	m_bDraw = state;
}

Boolean LeavingParticle::IsLocalLighting( void ) const
{
	return m_bLocalLighting;
}

void LeavingParticle::SetLocalLighting( Boolean state )
{
	m_bLocalLighting = state;
}

UInt32 LeavingParticle::GetTransparencyPriority( void ) const
{
	return m_Subset.priority;
}

void LeavingParticle::SetTransparencyPriority( UInt32 priority )
{
	m_Subset.priority = priority;
}

Mix::Scene::WATER_CONTAINS_RESULT LeavingParticle::GetDefaultWaterContainsResult( void ) const
{
	return m_DefWCResult;
}

void LeavingParticle::SetDefaultWaterContainsResult( Mix::Scene::WATER_CONTAINS_RESULT result )
{
	m_DefWCResult = result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IRendererObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Scene::IRendererObject::TYPE LeavingParticle::GetType( void ) const
{
	return Mix::Scene::IRendererObject::LEAVING_PARTICLE;
}

Boolean LeavingParticle::IsRendering( void ) const
{
	return RendererObject::IsRendering();
}

}}}
