// BlackHole.cpp: implementation of the CBlackHole class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "BlackHole.h"

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

#define		PLANETNBR				300
#define		PARTICLENBR				3000

#define		MAXDISTANCE				10.0f
#define		DISTVARINACE			0.25f

#define		POWBASE					2.0f
#define		GRAVITYBASE				0.05f
#define		ACCELBASE				0.1f

#define		BASEOMEGA				0.1f

#define		THROWACCEL				1.2f

VOID ProcessObjects( POBJECT po, DWORD dwObjectsCount, FLOAT fElapsed )
{
	for( DWORD i = 0 ; i < dwObjectsCount ; i++ )
	{
		if( !po[i].m_bThrowAway )
		{		
			if( po[i].m_fDistance < 1.0f )
			{
				po[i].m_bThrowAway = TRUE;
				po[i].m_vDir = Normalize( po[i].m_vPos + he3d_CVector( 0.0f, 20.0f, 0.f ) );
				po[i].m_fDistance = 0.5f;
				po[i].m_fOmega = 1.0f + 0.5f*RAND();

				if( RAND() < 0.5f )
					po[i].m_vDir.y = -po[i].m_vDir.y;			
			}
		}
		else
		{		
			if( po[i].m_fDistance > 15.0f )
			{
				po[i].m_bThrowAway = FALSE;
				po[i].m_fAlpha = H_2PI*RAND();
				po[i].m_fDistance = MAXDISTANCE*( 1.0f - DISTVARINACE )  + DISTVARINACE*MAXDISTANCE*RAND();
				po[i].m_fOmega = BASEOMEGA*RAND();
				po[i].m_fScale = 0.02f + 0.05f*RAND();
			}
		}			

		if( !po[i].m_bThrowAway )
		{		
			po[i].m_fDistance -= GRAVITYBASE*MAXDISTANCE*fElapsed/po[i].m_fDistance;

			if( po[i].m_fDistance < 4.0f )
				po[i].m_fDistance -= fElapsed;

			if( po[i].m_fDistance < 0.1f )
				po[i].m_fDistance = 0.1f;

			po[i].m_fAlpha += ACCELBASE*po[i].m_fOmega*powf( POWBASE, (MAXDISTANCE - po[i].m_fDistance) )*fElapsed;		
			po[i].m_vPos = po[i].m_fDistance*he3d_CVector( sinf( po[i].m_fAlpha ), 0.0f, cosf( po[i].m_fAlpha ) );			
		}
		else
		{
			po[i].m_fDistance += fElapsed*powf( po[i].m_fOmega, po[i].m_fDistance );
			po[i].m_vPos = po[i].m_vDir*po[i].m_fDistance;			
		}
	}
}

CBlackHole::CBlackHole( PDIRECT3DDEVICE8 pDevice ) : CEfx( pDevice )
{
	m_pvbParticle = NULL;
	m_pvbBeams = NULL;
	m_pvbHorizont = NULL;
	m_piqFade = NULL;
	m_piqText = NULL;

	if( FAILED( m_pDevice->CreateVertexBuffer( PARTICLENBR*sizeof(D3DVECTOR), 0, D3DFVF_XYZ, D3DPOOL_DEFAULT, &m_pvbParticle ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create particle vertex buffer" );

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

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

	if( FAILED( m_pDevice->CreateVertexBuffer( 6000*sizeof(SIMPLEVERTEX), 0, FVF_SIMPLEVERTEX, D3DPOOL_DEFAULT, &m_pvbBeams ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create beams vertex buffer" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 102*sizeof(LVERTEX), 0, FVF_LVERTEX, D3DPOOL_DEFAULT, &m_pvbHorizont ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create horizont vertex buffer" );

	m_piqFade = new CImageQuad( m_pDevice, 0, 0, 512, 512 );
	m_piqFade->SetColor( 0x0 );
	m_piqText = new CImageQuad( m_pDevice, 100, 150, 412, 362 );
	m_piqText->SetColor( 0xffffff );

	PLVERTEX				pVertices;
	FLOAT					fAngle;

	m_pvbHorizont->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );
	
	pVertices[0] = LVERTEX( 0.0f, 0.0f, 0.0f, 0xffffffff, 0.5f, 0.5f );

	for( DWORD i = 1 ; i < 102 ; i++ )
	{	
		fAngle = (FLOAT)(i - 1)*H_2PI/100.0f;
		pVertices[i] = LVERTEX( cosf( fAngle ), 0.0f, sinf( fAngle ), 0x60ffffff, 0.5f*(cosf( fAngle ) + 1.0f), 0.5f*(sinf( fAngle ) + 1.0f) );
	}

	m_pvbHorizont->Unlock();

	try
	{
		LoadTexture( "exotic.jpg" );
		LoadTexture( "deepspace.jpg" );
		LoadTexture( "moon.jpg" );
		LoadTexture( "horizont.jpg" );
		LoadTexture( "text.jpg" );
	}
	catch( CTextureException )
	{
		throw CSystemException( DEMO_EXCEPTION_FILENOTFOUND, "unable to load textures" );
	}

	m_poParticle = new OBJECT[PARTICLENBR];

	for( i = 0 ; i < PARTICLENBR ; i++ )
	{
		m_poParticle[i].m_bThrowAway = FALSE;
		m_poParticle[i].m_fAlpha = H_2PI*RAND();
		m_poParticle[i].m_fDistance = 4.0f + 6.0f*RAND();
		m_poParticle[i].m_fOmega = 0.25f*RAND();
	}

	m_poPlanets = new OBJECT[PLANETNBR];

	for( i = 0 ; i < PLANETNBR ; i++ )
	{
		m_poPlanets[i].m_bThrowAway = FALSE;
		m_poPlanets[i].m_fAlpha = H_2PI*RAND();
		m_poPlanets[i].m_fDistance = 4.0f + 6.0f*RAND();
		m_poPlanets[i].m_fOmega = 0.25f*RAND();
		m_poPlanets[i].m_fScale = 0.02f + 0.05f*RAND();
	}	

	for( i = 0 ; i < 3000 ; i++ )	
	{	
		m_vBeams[i] = Polar2Cartesian3d( 15.0f*RAND(), H_2PI*RAND(), H_2PI*RAND() )*RotationMtx( H_2PI*RAND(), H_2PI*RAND(), H_2PI*RAND() );			
		m_fBornTime[i] = 0.0f;
	}

	m_fLastUpdate = 0.0f;
}

CBlackHole::~CBlackHole()
{		
	if( m_pvbParticle )
		m_pvbParticle->Release();

	if( m_pvbBeams )
		m_pvbBeams->Release();

	if( m_pvbHorizont )
		m_pvbHorizont->Release();

	if( m_piqFade )
		delete m_piqFade;

	if( m_piqText )
		delete m_piqText;
}

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

BOOL CBlackHole::FinishEfx()
{
	return TRUE;
}

BOOL CBlackHole::UpdateFrame( FLOAT fTime )
{
	FLOAT					fElapsed = fTime - m_fLastUpdate;
	he3d_CVector			vPos = he3d_CVector( 0.0f, 5.0f, -15.0f )*YRotationMtx( 0.4f*fTime )*ScaleMtx( 1.0f - 0.5f*sinf( H_PI2*fTime/5.0f ) );

	m_fLastUpdate = fTime;

	m_pDevice->SetTransform( D3DTS_PROJECTION, ProjectionMtx( 45.0f, 0.75f, 0.5f, 1000.0f ) );
	m_pDevice->SetTransform( D3DTS_VIEW, CameraMtx( vPos, he3d_CVector( 0.0f, 0.0f, 0.0f ), 15.0f*sinf( H_2PI*fTime/15.0f ) ) );


	ProcessObjects( m_poParticle, PARTICLENBR, fElapsed );
	ProcessObjects( m_poPlanets, PLANETNBR, fElapsed );

	D3DVECTOR*				pVertices;	

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

	for( DWORD i = 0 ; i < PARTICLENBR ; i++ )
		*(pVertices++) = m_poParticle[i].m_vPos;	

	m_pvbParticle->Unlock();

	m_fFactor = fTime;

	PSIMPLEVERTEX			pVerts;
	FLOAT					fAlpha;	
	FLOAT					fVal;
	he3d_CVector			v, n;

	m_pvbBeams->Lock( 0, 0, (LPBYTE*)&pVerts, 0 );

	for( i = 0 ; i < 3000 ; i++ )
	{
		fAlpha = 255.0f;

		if( fTime - m_fBornTime[i] < 1.0f )
			fAlpha = 255.0f*(fTime - m_fBornTime[i]);

		n = Normalize( m_vBeams[i] );
		v = m_vBeams[i] - 2.0f*( fTime - m_fBornTime[i] )*n;

		if( ( fVal = Length( v ) ) < 2.0f )		
			fAlpha = fVal*128.0f;
		
		if( fAlpha < 0.0f )
			fAlpha = 0.0f;
		else if( fAlpha > 255.0f )
			fAlpha = 255.0f;

		pVerts[2*i+0].m_vPos = v;
		pVerts[2*i+0].m_dwColor = D3DCOLOR_ARGB( (DWORD)fAlpha, 0x80, 0x0, 0xff );

		pVerts[2*i+1].m_vPos = v + n;
		pVerts[2*i+1].m_dwColor = 0x0;

		if( fVal < 1.0f )
		{
			m_vBeams[i] = Polar2Cartesian3d( 10.0f + 5.0f*RAND(), H_2PI*RAND(), H_2PI*RAND() )*RotationMtx( H_2PI*RAND(), H_2PI*RAND(), H_2PI*RAND() );			
			m_fBornTime[i] = fTime;
		}
	}

	m_pvbBeams->Unlock();

	fAlpha = 0.0f;

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

	if( fTime > 15.0f )	
		fAlpha = (fTime - 15.0f)*64.0f;	

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

	m_piqFade->SetAlpha( (DWORD)fAlpha );

	fAlpha = 0.0f;

	if( fTime >= 10.0f )
		fAlpha = 255.0f*sinf( H_PI*(fTime - 10.0f)/15.0f );

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

	m_piqText->SetAlpha( (DWORD)fAlpha );

	return TRUE;
}

BOOL CBlackHole::RenderEfx()
{	
	m_pDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

	m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

	m_pDevice->SetTexture( 0, GetTexture( "deepspace.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 0.15f) );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
	m_pDevice->SetTransform( D3DTS_TEXTURE0, TranslationMtx( 1.0f, 1.0f, 1.0f )*ScaleMtx( 0.15f ) );	
	m_mSphere.Render( m_pDevice );		
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );	

	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

	m_pDevice->SetTexture( 0, NULL );
	m_pDevice->SetTransform( D3DTS_WORLD, IdentMtx() );

	m_pDevice->SetVertexShader( D3DFVF_XYZ );
	m_pDevice->SetStreamSource( 0, m_pvbParticle, sizeof(D3DVECTOR) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, PARTICLENBR );

	m_pDevice->SetTexture( 0, GetTexture( "moon.jpg" ) );

	for( DWORD i = 0 ; i < PLANETNBR ; i++ )
	{
		m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( m_poPlanets[i].m_fScale )*TranslationMtx( m_poPlanets[i].m_vPos ) );
		m_mAsteroid.Render( m_pDevice );
	}

	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

	m_pDevice->SetTexture( 0, GetTexture( "exotic.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 0.005f) );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
	m_pDevice->SetTransform( D3DTS_TEXTURE0, RotationMtx( 0.5f*m_fFactor, -0.5f*m_fFactor, 0.5f*m_fFactor )*TranslationMtx( 1.0f, 1.0f, 1.0f )*ScaleMtx( 0.005f ) );	
	m_mSphere.Render( m_pDevice );		
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );	

	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

	m_pDevice->SetTexture( 0, GetTexture( "horizont.jpg" ) );
	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 20.0f )*TranslationMtx( -0.5f, 0.0f, 0.0f ) );

	m_pDevice->SetVertexShader( FVF_LVERTEX );
	m_pDevice->SetStreamSource( 0, m_pvbHorizont, sizeof(LVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 100 );

	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );

	m_pDevice->SetTexture( 0, NULL );
	m_pDevice->SetTransform( D3DTS_WORLD, IdentMtx() );	
	m_pDevice->SetVertexShader( FVF_SIMPLEVERTEX );
	m_pDevice->SetStreamSource( 0, m_pvbBeams, sizeof(SIMPLEVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_LINELIST, 0, 2000 );

	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );	

	m_piqFade->Render( m_pDevice );

	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

	m_pDevice->SetTexture( 0, GetTexture( "text.jpg" ) );
	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );

	m_piqText->Render( m_pDevice );

	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

	return TRUE;
}