#include "stdafx.h"
#include "TrailGeometry.h"

#include "RenderSystem.h"
#include "CameraManager.h"
#include "FogAgent.h"

cRibbonGeometry::cRibbonGeometry()
: mMaxVerts( 0 )
, mNumVerts( 0 )
, mPushEnabled( true )
, mPushInterval( 0.0f )
, mPushTime( 0.0f )
, mAlphaInterval( 0.0f )
, mAlphaTime( 0.0f )
, mAlphaFactor( 0.0f )
, mTexCoordFlip( false )
, mTexture( 0 )
, mVertexDeclaration( 0 )
, mPosCoordBuffer( 0 )
, mTexCoordBuffer( 0 )
, mAlphaBuffer( 0 )
{
	mOldPoint[0] = mOldPoint[1] = NiPoint3::ZERO;
	mPoint[0] = mPoint[1] = NiPoint3::ZERO;

//	OutputDebugString( _T("riboon create\n") );
}

cRibbonGeometry::~cRibbonGeometry()
{
	Clear();

//	OutputDebugString( _T("riboon delete\n") );
}

void cRibbonGeometry::Clear()
{
	/// 정점 선언 및 버퍼를 제거
	if( mVertexDeclaration )
	{
		mVertexDeclaration->Release();
		mVertexDeclaration = 0;
	}
	if( mPosCoordBuffer )
	{
		mPosCoordBuffer->Release();
		mPosCoordBuffer = 0;
	}
	if( mTexCoordBuffer )
	{
		mTexCoordBuffer->Release();
		mTexCoordBuffer = 0;
	}
	if( mAlphaBuffer )
	{
		mAlphaBuffer->Release();
		mAlphaBuffer = 0;
	}

	///
	mMaxVerts = 0;
	mNumVerts = 0;
	mPushInterval = 0.0f;
	mPushTime = 0.0f;
	mAlphaInterval = 0.0f;
	mAlphaTime = 0.0f;
	mAlphaFactor = 0.0f;
}

bool cRibbonGeometry::Init( unsigned int maxVerts, float pushInterval, float alphaInterval, float alphaFactor )
{
	assert( maxVerts >= 4 );

	Clear();

	mMaxVerts = maxVerts;
	mPushInterval = pushInterval;
	mAlphaInterval = alphaInterval;
	mAlphaFactor = alphaFactor;

	/// 셰이더를 초기화
	cRenderer* renderer = RENDERSYS->GetRenderer();
	mShader.Init( renderer->GetTrailEffect() );

	/// 정점 선언을 생성
	D3DVERTEXELEMENT9 decl[] =
	{
		{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
		{ 1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
		{ 2, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
		D3DDECL_END()
	};

	LPDIRECT3DDEVICE9 device = renderer->GetD3DDevice();
	device->CreateVertexDeclaration( decl, &mVertexDeclaration );

	/// 정점 버퍼를 생성
	mPosCoordBuffer = renderer->CreateVertexBuffer( mMaxVerts, sizeof(NiPoint3), 0 );
	if( mPosCoordBuffer == 0 )
		return false;

	mTexCoordBuffer = renderer->CreateVertexBuffer( mMaxVerts, sizeof(NiPoint2), 0 );
	if( mTexCoordBuffer == 0 )
		return false;

	mAlphaBuffer = renderer->CreateVertexBuffer( mMaxVerts, sizeof(float), 0 );
	if( mAlphaBuffer == 0 )
		return false;


	return true;
}

void cRibbonGeometry::Process( float deltaTime )
{
	/// 정점을 추가
	if( mPushEnabled )
	{
		mPushTime += deltaTime;

//		if( mPushTime > mPushInterval )
		{
			mPushTime = 0.0f;

			if( mNumVerts + 2 <= mMaxVerts && (mOldPoint[0] != mPoint[0] || mOldPoint[1] != mPoint[1]) )
			{
				unsigned int length = (mNumVerts + 2) * sizeof(NiPoint3);
				void* p = 0;
				HRESULT ret = mPosCoordBuffer->Lock( 0, length, &p, 0 );

				if( SUCCEEDED(ret) )
				{
					NiPoint3* pos = (NiPoint3*)p;

					for( int i = mNumVerts-1; i > 0; i -= 2 )
					{
						pos[i+1] = pos[i-1];
						pos[i+2] = pos[i];
					}

					pos[0] = mOldPoint[0] = mPoint[0];
					pos[1] = mOldPoint[1] = mPoint[1];
				}
				mPosCoordBuffer->Unlock();

				///
				length = (mNumVerts + 2) * sizeof(NiPoint2);
				p = 0;
				ret = mTexCoordBuffer->Lock( 0, length, &p, 0 );

				if( SUCCEEDED(ret) )
				{
					NiPoint2* tex = (NiPoint2*)p;

					unsigned int i = 0, i2 = 0;
					unsigned int iend = mNumVerts / 2;
					float texUnit = 1.0f / iend;

					for( ; i <= iend; ++i, i2 += 2 )
					{
						tex[i2  ].x = tex[i2+1].x = i * texUnit;
						tex[i2  ].y = 0.0f;
						tex[i2+1].y = 1.0f;
					}
				}
				mTexCoordBuffer->Unlock();

				///
				length = (mNumVerts + 2) * sizeof(float);
				p = 0;
				ret = mAlphaBuffer->Lock( 0, length, &p, 0 );
				if( SUCCEEDED(ret) )
				{
					float* alpha = (float*)p;

					for( int i = mNumVerts-1; i > 0; i -= 2 )
					{
						alpha[i+1] = alpha[i-1];
						alpha[i+2] = alpha[i];
					}

					alpha[0] = 1.0f;
					alpha[1] = 1.0f;
				}
				mAlphaBuffer->Unlock();

				mNumVerts += 2;
			}
		}
	}

	/// 알파 배열을 갱신
	if( mNumVerts == 0 )
		return;

	mAlphaTime += deltaTime;

	if( mAlphaTime > mAlphaInterval )
	{
		float da = mAlphaTime * mAlphaFactor;
		mAlphaTime = 0.0f;

		//
		unsigned int c = 0;
		unsigned int length = mNumVerts * sizeof(float);
		void* p = 0;
		HRESULT ret = mAlphaBuffer->Lock( 0, length, &p, 0 );

		if( SUCCEEDED(ret) )
		{
			float* alpha = (float*)p;

			for( unsigned int i = 0; i < mNumVerts; i += 2 )
			{
				alpha[i  ] -= da;
				alpha[i+1] -= da;

				if( alpha[i] < 0.0f )
				{
					c += 2;
				}
			}
		}
		mAlphaBuffer->Unlock();
		mNumVerts -= c;

		if( c > 0 && mNumVerts >= 4 )
		{
			p = 0;
			ret = mTexCoordBuffer->Lock( 0, mNumVerts * sizeof(NiPoint2), &p, 0 );

			if( SUCCEEDED(ret) )
			{
				NiPoint2* tex = (NiPoint2*)p;

				unsigned int i = 0, i2 = 0;
				unsigned int iend = (mNumVerts-2) / 2;
				float texUnit = 1.0f / iend;

				for( ; i <= iend; ++i, i2 += 2 )
				{
					tex[i2  ].x = tex[i2+1].x = i * texUnit;
					tex[i2  ].y = 0.0f;
					tex[i2+1].y = 1.0f;
				}
			}
			mTexCoordBuffer->Unlock();
		}
	}
}

void cRibbonGeometry::RenderImmediate( NiRenderer* /*renderer*/ )
{
	if( mNumVerts < 4 )
		return;

	///
	cRenderer* renderer = RENDERSYS->GetRenderer();
	assert( renderer );

	if( renderer->IsDeviceLost() )
		return;

	/// 장치 설정을 보관
	LPDIRECT3DDEVICE9 device = renderer->GetD3DDevice();
	IDirect3DVertexDeclaration9* oldVertDecl = 0;
	device->GetVertexDeclaration( &oldVertDecl );

	/// 셰이더를 시작
	if( mShader.Begin( CAMERAMAN->GetCurrent(), renderer->GetD3DViewProj() ) == true )
	{
		mShader.SetFog( FOGAGENT->GetFogProperty() );
		mShader.CommitChanges();


		///
		device->SetTexture( 0, mTexture );
		device->SetVertexDeclaration( mVertexDeclaration );
		device->SetStreamSource( 0, mPosCoordBuffer, 0, sizeof(NiPoint3) );
		device->SetStreamSource( 1, mTexCoordBuffer, 0, sizeof(NiPoint2) );
		device->SetStreamSource( 2, mAlphaBuffer, 0, sizeof(float) );

		device->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, mNumVerts-2 );

		mShader.End();
	}

	/// 장치 설정을 복원
	if( oldVertDecl )
		device->SetVertexDeclaration( oldVertDecl );
}

/*
void cRibbonGeometry::Process( float deltaTime )
{
	/// 정점을 추가
	if( mPushEnabled )
	{
		mPushTime += deltaTime;

		if( mPushTime > mPushInterval )
		{
			mPushTime = 0.0f;

			if( mNumVerts + 2 <= mMaxVerts && (mOldPoint[0] != mPoint[0] || mOldPoint[1] != mPoint[1]) )
			{

				unsigned int length = (mNumVerts + 2) * sizeof(NiPoint3);
				void* p = 0;
				HRESULT ret = mPosCoordBuffer->Lock( 0, length, &p, 0 );

				if( SUCCEEDED(ret) )
				{
					NiPoint3* pos = (NiPoint3*)p;

					for( int i = mNumVerts-1; i > 0; i -= 2 )
					{
						pos[i+1] = pos[i-1];
						pos[i+2] = pos[i];
					}

					pos[0] = mOldPoint[0] = mPoint[0];
					pos[1] = mOldPoint[1] = mPoint[1];
				}
				mPosCoordBuffer->Unlock();

				///
				length = (mNumVerts + 2) * sizeof(NiPoint2);
				p = 0;
				ret = mTexCoordBuffer->Lock( 0, length, &p, 0 );

				if( SUCCEEDED(ret) )
				{
					NiPoint2* tex = (NiPoint2*)p;

					for( unsigned int i = 0; i < mNumVerts; i += 2 )
					{
						tex[i  ].x = 0.0f;
						tex[i  ].y = 0.0f;
						tex[i+1].x = 0.0f;
						tex[i+1].y = 1.0f;
					}
					tex[mNumVerts  ].x = 1.0f;
					tex[mNumVerts  ].y = 0.0f;
					tex[mNumVerts+1].x = 1.0f;
					tex[mNumVerts+1].y = 1.0f;
				}
				mTexCoordBuffer->Unlock();

				///
				length = (mNumVerts + 2) * sizeof(float);
				p = 0;
				ret = mAlphaBuffer->Lock( 0, length, &p, 0 );
				if( SUCCEEDED(ret) )
				{
					float* alpha = (float*)p;

					for( int i = mNumVerts-1; i > 0; i -= 2 )
					{
						alpha[i+1] = alpha[i-1];
						alpha[i+2] = alpha[i];
					}

					alpha[0] = 1.0f;
					alpha[1] = 1.0f;
				}
				mAlphaBuffer->Unlock();

				mNumVerts += 2;
			}
		}
	}

	/// 알파 배열을 갱신
	if( mNumVerts == 0 )
		return;

	mAlphaTime += deltaTime;

	if( mAlphaTime > mAlphaInterval )
	{
		float da = mAlphaTime * mAlphaFactor;
		mAlphaTime = 0.0f;

		//
		unsigned int c = 0;
		unsigned int length = mNumVerts * sizeof(float);
		void* p = 0;
		HRESULT ret = mAlphaBuffer->Lock( 0, length, &p, 0 );

		if( SUCCEEDED(ret) )
		{
			float* alpha = (float*)p;

			for( unsigned int i = 0; i < mNumVerts; i += 2 )
			{
				alpha[i  ] -= da;
				alpha[i+1] -= da;

				if( alpha[i] < 0.0f )
				{
					c += 2;
				}
			}
		}
		mAlphaBuffer->Unlock();
		mNumVerts -= c;

		///
		if( c > 0 && mNumVerts >= 4 )
		{
			p = 0;
			ret = mTexCoordBuffer->Lock( sizeof(NiPoint2) * (mNumVerts-2), sizeof(NiPoint2) * 2, &p, 0 );

			if( SUCCEEDED(ret) )
			{
				NiPoint2* tex = (NiPoint2*)p;

				tex[0].x = 1.0f;
				tex[0].y = 0.0f;
				tex[1].x = 1.0f;
				tex[1].y = 1.0f;
			}
			mTexCoordBuffer->Unlock();
		}
	}
}
//*/
