// Flora.cpp: implementation of the CFlora class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Flora.h"

//////////////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////////////

he3d_CVector GeneratePos()
{
	FLOAT					fRadius = RAND();
	FLOAT					a = H_2PI*RAND();
	
	return he3d_CVector( fRadius*cosf(a), -1.0f, fRadius*sinf(a) );
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFlora::CFlora( PDIRECT3DDEVICE8 pDevice ) : CEfx( pDevice )
{		
	try
	{
		pScene = new he3d_CScene();
		pScene->Initialize( m_pDevice );
		pScene->Load( "data\\scenes\\flora.scene" );
 		pScene->SetVieport( 1.0f, 1000.0f, 1.0f );
	}
	catch( he3d_CException )
	{
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to load flora.scene" );
	}

	try
	{
		LoadTexture( "flare2.jpg" );
		LoadTexture( "cloud2.jpg" );
		LoadTexture( "flare0.bmp" );
	}
	catch( CTextureException )
	{
		throw CSystemException( DEMO_EXCEPTION_FILENOTFOUND, "unable to load textures" );
	}

	if( !m_mSky.Load( m_pDevice, "data\\objects\\detailsphere.smf" ) )
		throw CSystemException( DEMO_EXCEPTION_FILENOTFOUND, "unable to load detailsphere.smf" );

	if( !m_psSeed.Initialize( m_pDevice ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to initialize seeds particle system" );		

	m_pvbVolumeHull = NULL;
	m_pvbEnergyBeam = NULL;
	m_piqFade = NULL;

	if( FAILED( m_pDevice->CreateVertexBuffer( 60*sizeof(SIMPLEVERTEX), 0, FVF_SIMPLEVERTEX, D3DPOOL_DEFAULT, &m_pvbVolumeHull ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create volume hull vertex buffer" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 300*sizeof(SIMPLEVERTEX), D3DUSAGE_DYNAMIC, FVF_SIMPLEVERTEX, D3DPOOL_DEFAULT, &m_pvbEnergyBeam ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create energy beam vertex buffer" );

	m_piqFade = new CImageQuad( m_pDevice, 0, 0, 512, 512 );
	m_piqFade->SetColor( 0xffffff );

	for( DWORD i = 0 ; i < 5 ; i++ )
	{
		DWORD				dwEmit = 100 + rand()%100;

		m_peFlowers[i].SetEmitAngle( 0.6f );
		m_peFlowers[i].SetEmiterDir( he3d_CVector( 0, 1, 0 ) );
		m_peFlowers[i].SetVelocity( 3.5f, 0.5f );
		m_peFlowers[i].SetSize( 2.5f, 0.5f );
		m_peFlowers[i].SetDimension( dwEmit );
		m_peFlowers[i].SetEmitCount( dwEmit, 0 );
		m_peFlowers[i].SetParticleType( PT_STANDARD );		

		m_psSeed.AddEmiter( &m_peFlowers[i] );
	}

	m_peFlowers[0].SetEmitInterval( 6.4f );
	m_peFlowers[1].SetEmitInterval( 15.5f );
	m_peFlowers[2].SetEmitInterval( 10.5f );
	m_peFlowers[3].SetEmitInterval( 8.2f );
	m_peFlowers[4].SetEmitInterval( 12.5f );

	m_psSeed.ConfigureGravity( G_VECTOR, he3d_CVector( 0, -1, 0 ), 2.0f );
	m_psSeed.SetTexture( GetTexture( "flare2.jpg" ) );

	he3d_CMaterial*			mat;

	mat = pScene->GetMaterial( "WING00" );

	mat->a = 0.3f;
	mat->dwFlags = FLAG_ALPHABLEND;
	mat->dwAlphaOp = ALPHAOP_LINEAR;	

	mat = pScene->GetMaterial( "WING01" );

	mat->a = 0.3f;
	mat->dwFlags = FLAG_ALPHABLEND;
	mat->dwAlphaOp = ALPHAOP_LINEAR;	

	for( i = 0 ; i < 10 ; i++ )
	{	
		FLOAT				fAngle = (FLOAT)i*H_2PI/10.0f;

		m_vShiluette[i] = he3d_CVector( cosf(fAngle), 0.0f, sinf(fAngle) );
	}

	PSIMPLEVERTEX			pVertices;

	m_pvbVolumeHull->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );
	CreateParallelVolumeFromShiluetteStrip( 10, m_vShiluette, he3d_CVector( 0, 1, 0 ), sizeof(SIMPLEVERTEX), (LPBYTE)pVertices );

	for( i = 0 ; i < 60 ; i++ )	
		pVertices[i].m_dwColor = 0x20ffffff;			

	m_pvbVolumeHull->Unlock();

	m_vsVolume1[0] = VECTORKEY(  0.0f, 0.0f, 0.0f, 0.0f );
	m_vsVolume1[1] = VECTORKEY( 13.2f, 0.2f, 0.0f, 0.2f );
	m_vsVolume1[2] = VECTORKEY( 19.2f, 0.2f, 2.0f, 0.2f );
	m_vsVolume1[3] = VECTORKEY( 24.0f, 5.0f, 2.0f, 5.0f );

	m_vsVolume2[0] = VECTORKEY(  0.0f, 0.0f, 0.0f, 0.0f );
	m_vsVolume2[1] = VECTORKEY(  3.6f, 0.2f, 0.0f, 0.2f );
	m_vsVolume2[2] = VECTORKEY(  6.0f, 0.2f, 2.0f, 0.2f );
	m_vsVolume2[3] = VECTORKEY(  8.4f, 1.0f, 2.0f, 1.0f );

	m_vsVolume3[0] = VECTORKEY(  0.0f, 0.0f, 0.0f, 0.0f );
	m_vsVolume3[1] = VECTORKEY(  8.4f, 0.2f, 0.0f, 0.2f );
	m_vsVolume3[2] = VECTORKEY( 10.8f, 0.2f, 2.0f, 0.2f );
	m_vsVolume3[3] = VECTORKEY( 13.2f, 1.0f, 2.0f, 1.0f );

	m_vsVolume4[0] = VECTORKEY(  0.0f, 0.0f, 0.0f, 0.0f );
	m_vsVolume4[1] = VECTORKEY(  9.6f, 0.2f, 0.0f, 0.2f );
	m_vsVolume4[2] = VECTORKEY( 12.0f, 0.2f, 2.0f, 0.2f );
	m_vsVolume4[3] = VECTORKEY( 14.4f, 1.0f, 2.0f, 1.0f );

	m_vsVolume5[0] = VECTORKEY(  0.0f, 0.0f, 0.0f, 0.0f );
	m_vsVolume5[1] = VECTORKEY( 13.2f, 0.2f, 0.0f, 0.2f );
	m_vsVolume5[2] = VECTORKEY( 15.6f, 0.2f, 2.0f, 0.2f );
	m_vsVolume5[3] = VECTORKEY( 18.0f, 1.0f, 2.0f, 1.0f );

	InitVectorSpline( 4, m_vsVolume1 );
	InitVectorSpline( 4, m_vsVolume2 );
	InitVectorSpline( 4, m_vsVolume3 );
	InitVectorSpline( 4, m_vsVolume4 );
	InitVectorSpline( 4, m_vsVolume5 );

	m_pvbEnergyBeam->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );

	for( i = 0 ; i < 300 ; i++ )
	{
		m_pfVelocity[i] = 5.0f + 5.0f*RAND();
		m_pfBornTime[i] = -10.0f;
		m_pvPos[i] = GeneratePos();
		pVertices[i].m_dwColor = 0x80ffffff;
	}

	m_pvbEnergyBeam->Unlock();
}

CFlora::~CFlora()
{	
	if( m_pvbVolumeHull )
		m_pvbVolumeHull->Release();

	if( m_pvbEnergyBeam )
		m_pvbEnergyBeam->Release();

	if( m_piqFade )
		delete m_piqFade;

	//TODO: fix memory leak during skin object removal !!!!!!!!!!!!!!!!!!!!!!!!! (he3d_CSkin::~he3d_CSkin) !!!!!!!!!!!!!!
	//delete pScene;
}

BOOL CFlora::InitEfx()
{
	return TRUE;
}

BOOL CFlora::FinishEfx()
{
	pScene->CloseScene();

	return TRUE;
}

BOOL CFlora::UpdateFrame( FLOAT fTime )
{
	pScene->Transform( 25.0f*fTime );
	m_psSeed.Update( fTime );

	he3d_CKeyframer*		keyf;

	keyf = pScene->GetKeyframer( "INNERFLOWER00" );
	m_peFlowers[0].SetEmiterPos( he3d_CVector( 0, 0, 0 )*keyf->mtxFrame + he3d_CVector( 0, 0.5f, 0 ) );

	keyf = pScene->GetKeyframer( "INNERFLOWER01" );
	m_peFlowers[1].SetEmiterPos( he3d_CVector( 0, 0, 0 )*keyf->mtxFrame + he3d_CVector( 0, 0.5f, 0 ) );

	keyf = pScene->GetKeyframer( "INNERFLOWER02" );
	m_peFlowers[2].SetEmiterPos( he3d_CVector( 0, 0, 0 )*keyf->mtxFrame + he3d_CVector( 0, 0.5f, 0 ) );

	keyf = pScene->GetKeyframer( "INNERFLOWER03" );
	m_peFlowers[3].SetEmiterPos( he3d_CVector( 0, 0, 0 )*keyf->mtxFrame + he3d_CVector( 0, 0.5f, 0 ) );

	keyf = pScene->GetKeyframer( "INNERFLOWER04" );
	m_peFlowers[4].SetEmiterPos( he3d_CVector( 0, 0, 0 )*keyf->mtxFrame + he3d_CVector( 0, 0.5f, 0 ) );

	m_vVolumeScale[0] = GetVectorSplineValue( fTime, 4, m_vsVolume1 );
	m_vVolumeScale[1] = GetVectorSplineValue( fTime, 4, m_vsVolume2 );
	m_vVolumeScale[2] = GetVectorSplineValue( fTime, 4, m_vsVolume3 );
	m_vVolumeScale[3] = GetVectorSplineValue( fTime, 4, m_vsVolume4 );
	m_vVolumeScale[4] = GetVectorSplineValue( fTime, 4, m_vsVolume5 );

	PSIMPLEVERTEX			pVertices;
	FLOAT					fHeight;

	m_pvbEnergyBeam->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );

	for( DWORD i = 0 ; i < 300 ; i++ )
	{
		fHeight = m_pfVelocity[i]*( fTime - m_pfBornTime[i] ) - 1.0f;

		if( fHeight > 10.0f )
		{
			m_pfVelocity[i] = 5.0f + 5.0f*RAND();
			m_pfBornTime[i] = fTime;
			m_pvPos[i] = GeneratePos();
			fHeight = -1.0f;
		}

		pVertices[i].m_vPos = m_pvPos[i] + he3d_CVector( 0, fHeight, 0 );
	}

	m_pvbEnergyBeam->Unlock();

	FLOAT					fAlpha = 0.0f;

	if( fTime < 2.0f )			
		fAlpha = ( 2.0f - fTime )*128.0f;	

	if( fTime > 22.0f )
		fAlpha = ( fTime - 22.0f )*130.0f;	

	if( fAlpha < 0.0f )
		fAlpha = 0.0f;
	else if( fAlpha > 255.0f )
		fAlpha = 255.0f;	

	m_piqFade->SetAlpha( (DWORD)fAlpha );
		
	
	return TRUE;
}

BOOL CFlora::RenderEfx()
{
	m_pDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0xff, 1.0f, 0 );
	
	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );
	m_pDevice->SetTexture( 0, GetTexture( "cloud2.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, XRotationMtx( H_PI2 )*ScaleMtx( 0.3f ) );
	m_mSky.Render( m_pDevice );	

	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
	m_pDevice->SetRenderState( D3DRS_AMBIENT, 0xffffff );
	pScene->Render();		

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );	
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );

	m_pDevice->SetTexture( 0, NULL );
	

	m_pDevice->SetVertexShader( FVF_SIMPLEVERTEX );
	m_pDevice->SetStreamSource( 0, m_pvbVolumeHull, sizeof(SIMPLEVERTEX) );

	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[0] )*TranslationMtx( -12.5f, 0.0f, -15.5f ) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 20 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[1] )*TranslationMtx( -1.0f, 0.0f, 5.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 20 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[2] )*TranslationMtx( 10.0f, 0.0f, -7.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 20 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[3] )*TranslationMtx( -7.0f, 0.0f, 3.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 20 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[4] )*TranslationMtx( 8.0f, 0.0f, 2.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 20 );

	m_pDevice->SetTexture( 0, GetTexture( "flare0.bmp" ) );
	m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_POINTSIZE, FLT2DWORD( 2.0f ) );
	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );

	m_pDevice->SetStreamSource( 0, m_pvbEnergyBeam, sizeof(SIMPLEVERTEX) );	

	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[0] )*TranslationMtx( -12.5f, 0.0f, -15.5f ) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 300 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[1] )*TranslationMtx( -1.0f, 0.0f, 5.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 300 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[2] )*TranslationMtx( 10.0f, 0.0f, -7.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 300 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[3] )*TranslationMtx( -7.0f, 0.0f, 3.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 300 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_vVolumeScale[4] )*TranslationMtx( 8.0f, 0.0f, 2.0f ) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 300 );

	m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
	m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE, FALSE );
	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );	
	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );

	m_pDevice->SetTransform( D3DTS_WORLD, IdentMtx() );

	m_psSeed.Render( m_pDevice );

	m_pDevice->SetTexture( 0, NULL );
	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );	

	m_piqFade->Render( m_pDevice );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

	return TRUE;
}
