// MeteorAproaching.cpp: implementation of the CMeteorAproaching class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MeteorAproaching.h"

//////////////////////////////////////////////////////////////////////
// Helper function
//////////////////////////////////////////////////////////////////////

VOID CreateLensFlare( PLVERTEX& pVertices, FLOAT fS, he3d_CVector& p, he3d_CMatrix& mtx, FLOAT fScale, DWORD dwAlpha, DWORD dwColor = 0xffffff )
{	
	DWORD				dwARGB = ( dwAlpha<<24 ) | dwColor;

	*(pVertices++) = LVERTEX( (p + fScale*he3d_CVector( -fS, -fS, 0.0f ))*mtx, dwARGB, 0.0f, 1.0f );	
	*(pVertices++) = LVERTEX( (p + fScale*he3d_CVector( -fS,  fS, 0.0f ))*mtx, dwARGB, 0.0f, 0.0f );	
	*(pVertices++) = LVERTEX( (p + fScale*he3d_CVector(  fS, -fS, 0.0f ))*mtx, dwARGB, 1.0f, 1.0f );	

	*(pVertices++) = LVERTEX( (p + fScale*he3d_CVector(  fS, -fS, 0.0f ))*mtx, dwARGB, 1.0f, 1.0f );	
	*(pVertices++) = LVERTEX( (p + fScale*he3d_CVector( -fS,  fS, 0.0f ))*mtx, dwARGB, 0.0f, 0.0f );	
	*(pVertices++) = LVERTEX( (p + fScale*he3d_CVector(  fS,  fS, 0.0f ))*mtx, dwARGB, 1.0f, 0.0f );
}

BOOL SphereHit( he3d_CVector& vSource, he3d_CVector& vDir, he3d_CVector& vCenter, FLOAT fRadius )
{
	he3d_CVector			w = vSource - vCenter;
	FLOAT					a, b, c;
	FLOAT					delta;
	FLOAT					t1, t2;

	a = DotProd( vDir, vDir );
	b = 2.0f*DotProd( vDir, w );
	c = DotProd( w, w ) - fRadius*fRadius;

	delta = b*b - 4.0f*a*c;
	delta = sqrtf( delta );

	t1 = ( -b - delta )/2.0f*a;
	t2 = ( -b - delta )/2.0f*a;

	return t1 > 0.0f || t2 > 0.0f;
}

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

CMeteorAproaching::CMeteorAproaching( PDIRECT3DDEVICE8 pDevice ) : CEfx( pDevice )
{
	if( !m_mSphere.Load( m_pDevice, "data\\objects\\detailsphere.smf" ) )
		throw CSystemException( DEMO_EXCEPTION_FILENOTFOUND, "unable to load detatilsphere.smf" );	

	m_mSphere.FlipNormals();

	m_pvbFlare = NULL;
	m_pvbMeteorParticles = NULL;
	m_piqFade = NULL;

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

	if( FAILED( m_pDevice->CreateVertexBuffer( 300*sizeof(PARTICLEVERTEX), 0, FVF_PARTICLEVERTEX, D3DPOOL_DEFAULT, &m_pvbMeteorParticles ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create meteor particles vertex buffer" );

	if( !m_psMeteorSystem.Initialize( m_pDevice ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create meteor particle system" );	

	if( !m_psMeteorSystem2.Initialize( m_pDevice ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create meteor particle system 2" );	

	m_piqFade = new CImageQuad( m_pDevice );
	m_piqFade->SetColor( 0xffffff );
	
	try
	{
		LoadTexture( "deepspace.jpg" );
		LoadTexture( "moon.jpg" );
		LoadTexture( "youngearth.jpg" );
		LoadTexture( "sunflare.jpg" );
		LoadTexture( "redsmoke.jpg" );
		LoadTexture( "yellowsmoke.jpg" );
		LoadTexture( "smoke1.bmp" );

		for( DWORD i = 0 ; i < 4 ; i++ )
		{
			TCHAR			filename[] = "lens00.jpg";

			_stprintf( filename, "lens%02i.jpg", i );
			LoadTexture( filename );
			m_ptextLens[i] = GetTexture( filename );
		}
	}
	catch( CTextureException )
	{
		throw CSystemException( DEMO_EXCEPTION_FILENOTFOUND, "unable to load textures" );
	}

	m_psMeteorSystem.ConfigureGravity( G_VECTOR, Normalize( he3d_CVector( -10.0f, -5.0f, 10.0f ) - he3d_CVector( -2.5, 0.0f, -14.75f ) ), 4.0f );
	m_psMeteorSystem.SetTexture( GetTexture( "redsmoke.jpg" ) );

	m_psMeteorSystem2.ConfigureGravity( G_VECTOR, Normalize( he3d_CVector( -10.0f, -5.0f, 10.0f ) - he3d_CVector( -2.5, 0.0f, -14.75f ) ), 4.0f );
	m_psMeteorSystem2.SetTexture( GetTexture( "yellowsmoke.jpg" ) );

	for( DWORD i = 0 ; i < 7 ; i++ )
	{
		m_vEmiters[i] = Polar2Cartesian3d( 0.3f, H_2PI*RAND(), H_2PI*RAND() );

		m_peMeteorEmiters[i].SetEmitAngle( 0.10f );
		m_peMeteorEmiters[i].SetEmitCount( 1, 0 );
		m_peMeteorEmiters[i].SetEmiterDir( Normalize( he3d_CVector( -10.0f, -5.0f, 10.0f ) - he3d_CVector( -2.5, 0.0f, -14.75f ) ) );
		m_peMeteorEmiters[i].SetEmiterPos(  m_vEmiters[i] + he3d_CVector( -10.0f, -5.0f, 10.0f ) );
		m_peMeteorEmiters[i].SetLifeTime( 15.0f, 5.0f );
		m_peMeteorEmiters[i].SetSize( 2.0f, 0.0f );
		m_peMeteorEmiters[i].SetVelocity( 2.0f, 2.0f );
		m_peMeteorEmiters[i].SetParticleType( PT_STANDARD );
		m_peMeteorEmiters[i].SetEmitInterval( 0.1f );

		m_psMeteorSystem.AddEmiter( &m_peMeteorEmiters[i] );		
	}	

	for( i = 7 ; i < 10 ; i++ )
	{
		m_vEmiters[i] = Polar2Cartesian3d( 0.3f, H_2PI*RAND(), H_2PI*RAND() );

		m_peMeteorEmiters[i].SetEmitAngle( 0.10f );
		m_peMeteorEmiters[i].SetEmitCount( 1, 0 );
		m_peMeteorEmiters[i].SetEmiterDir( Normalize( he3d_CVector( -10.0f, -5.0f, 10.0f ) - he3d_CVector( -2.5, 0.0f, -14.75f ) ) );
		m_peMeteorEmiters[i].SetEmiterPos(  m_vEmiters[i] + he3d_CVector( -10.0f, -5.0f, 10.0f ) );
		m_peMeteorEmiters[i].SetLifeTime( 15.0f, 5.0f );
		m_peMeteorEmiters[i].SetSize( 2.0f, 0.0f );
		m_peMeteorEmiters[i].SetVelocity( 2.0f, 2.0f );
		m_peMeteorEmiters[i].SetParticleType( PT_STANDARD );
		m_peMeteorEmiters[i].SetEmitInterval( 0.1f );

		m_psMeteorSystem2.AddEmiter( &m_peMeteorEmiters[i] );		
	}	

	for( FLOAT fT = 0.0f ; fT < 5.0f ; fT += 0.1f )
	{
		m_psMeteorSystem.Update( fT );
		m_psMeteorSystem2.Update( fT );
	}

	m_vsCamTrg[0] = VECTORKEY( 0.0f, -10.0f, -25.0f, 10.0f );
	m_vsCamTrg[1] = VECTORKEY( 4.0f,   0.0f,  0.0f,  0.0f );
	m_vsCamTrg[2] = VECTORKEY( 10.0f,  0.0f,  0.0f,  0.0f );
	m_vsCamTrg[3] = VECTORKEY( 17.0f,  2.0f, -15.0f,  0.0f );	

	m_vsCamPos[0] = VECTORKEY(  0.0f, -5.0f,   0.0f, -20.0f );
	m_vsCamPos[1] = VECTORKEY(  6.0f, -5.0f,   5.0f, -20.0f );
	m_vsCamPos[2] = VECTORKEY( 12.0f, -5.0f,   5.0f, -20.0f );
	m_vsCamPos[3] = VECTORKEY( 17.0f, -5.0f,   5.0f, -30.0f );

	InitVectorSpline( 4, m_vsCamTrg );
	InitVectorSpline( 4, m_vsCamPos );

	PPARTICLEVERTEX			pVertices;

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

	for( i = 0 ; i < 300 ; i++ )
	{
		pVertices[i].p = Polar2Cartesian3d( 1.0f*RAND(), H_2PI*RAND(), H_2PI*RAND() );
		pVertices[i].size = 1.5f;
		pVertices[i].color = D3DCOLOR_ARGB( 0xff, 0xff, rand()%256, 0x0 );
	}

	m_pvbMeteorParticles->Unlock();
}

CMeteorAproaching::~CMeteorAproaching()
{
	if( m_pvbFlare )
		m_pvbFlare->Release();

	if( m_pvbMeteorParticles )
		m_pvbMeteorParticles->Release();

	if( m_piqFade )
		delete m_piqFade;
}

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

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

#define	LENSFACTOR	1.0f
#define	FOCUS		10.0f
#define	CONEFACTOR	0.02f

BOOL CMeteorAproaching::UpdateFrame( FLOAT fTime )
{
	m_psMeteorSystem.Update( fTime + 5.0f );
	m_psMeteorSystem2.Update( fTime + 5.0f );

	m_pDevice->SetTransform( D3DTS_PROJECTION, ProjectionMtx( 45.0f, 0.75f, 0.5f, 1000.0f ) );

	he3d_CVector			vPos = he3d_CVector( -5.0f, 5.0f, -20.0f );
	he3d_CVector			vTrg = GetVectorSplineValue( fTime, 4, m_vsCamTrg );

	he3d_CVector			v;

	he3d_CMatrix			mtx = CameraMtx( vPos, vTrg, 0.0f );

	m_pDevice->SetTransform( D3DTS_VIEW, mtx );

	m_vSunPos = he3d_CVector( 0.0f, 0.0f, 0.0f );
	
	m_mtxEarth = XRotationMtx( H_PI2 )*ScaleMtx( 0.03f )*YRotationMtx( 0.1f*fTime )*TranslationMtx( 0.0f, 0.0f, -15.0f )*
				 YRotationMtx( 0.01f*fTime );
	
	m_mtxMoon = XRotationMtx( H_PI2 )*ScaleMtx( 0.004f )*YRotationMtx( 0.01f*fTime )*TranslationMtx( -5.0f, 0.0f, 0.0f )*
			    YRotationMtx( -0.3f + 0.01f*fTime )*ZRotationMtx( -0.9f )*TranslationMtx( 0.0f, 0.0f, -15.0f )*
				YRotationMtx( 0.01f*fTime );;

	PLVERTEX				pVertices;

	FLOAT					fScale = 1.0f;
	DWORD					dwAlpha = 0xff;

	// rays setup

	he3d_CVector			vRays[100];
	he3d_CMatrix			mtxRays = InverseCameraMtx( CameraMtx( m_vSunPos, vPos, 0.0f ) );
	FLOAT					fVal;
	he3d_CVector			vMoon, vEarth;
	DWORD					dwOccluded = 100;	

	for( DWORD i = 0 ; i < 99 ; i++ )
	{
		fVal = (FLOAT)i/99.0f;

		vRays[i+1] = Normalize( he3d_CVector( CONEFACTOR*cosf( fVal ), CONEFACTOR*sinf( fVal ), 1.0f ) )*mtxRays;
	}

	vRays[0] = he3d_CVector( 0.0f, 0.0f, 1.0f )*mtxRays;

	for( i = 0 ; i < 100 ; i++ )
	{
		vMoon = m_mSphere.GetCenter()*m_mtxMoon;
		vEarth = m_mSphere.GetCenter()*m_mtxEarth;

		if( SphereHit( m_vSunPos, vRays[i], vEarth, 3.9f ) || SphereHit(  m_vSunPos, vRays[i], vMoon, 0.52f ) )
			dwOccluded--;
	}

	fScale = (FLOAT)dwOccluded/100.0f;
	dwAlpha = (DWORD)(255.0f*fScale);
	
	v = Normalize( he3d_CVector( 0.0f, 0.0f, FOCUS ) - m_vSunPos*mtx );
	mtx = InverseCameraMtx( mtx );
	
	// lens flares system

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

	CreateLensFlare( pVertices, 5.0f, he3d_CVector( 0.0f, 0.0f, 0.0f ), mtx, fScale, 0xff );	
	
	CreateLensFlare( pVertices, 3.0f,   1.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 3.0f,  -1.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 9.0f,  -4.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 1.0f,  -9.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 0.6f,   5.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 1.8f,   5.5f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 4.2f,   6.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 2.4f,   7.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 2.4f,   8.5f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 1.2f,   9.5f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xa0a0ff );
	CreateLensFlare( pVertices, 2.4f,  11.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xffa0a0 );
	CreateLensFlare( pVertices, 2.4f,  11.5f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xffa0a0 );
	CreateLensFlare( pVertices, 3.0f,  15.0f*LENSFACTOR*v, mtx, fScale, dwAlpha, 0xffa0a0 );

	CreateLensFlare( pVertices, 3.0f,  16.0f*LENSFACTOR*v, mtx, fScale, dwAlpha );
	CreateLensFlare( pVertices, 3.0f,   2.5f*LENSFACTOR*v, mtx, fScale, dwAlpha );
	CreateLensFlare( pVertices, 3.0f, -12.0f*LENSFACTOR*v, mtx, fScale, dwAlpha );

	CreateLensFlare( pVertices, 1.0f,  12.0f*LENSFACTOR*v, mtx, fScale, dwAlpha );
	CreateLensFlare( pVertices, 0.5f,  13.0f*LENSFACTOR*v, mtx, fScale, dwAlpha );
					
	m_pvbFlare->Unlock();

	v = he3d_CVector( -10.0f, -5.0f, 10.0f ) - 0.05f*fTime*m_peMeteorEmiters[0].GetEmiterDir();

	for( i = 0 ; i < 10 ; i++ )	
		m_peMeteorEmiters[i].SetEmiterPos( m_vEmiters[i] +  v );	

	m_mtxMeteor = RotationMtx( fTime, 0.5f*fTime, -fTime )*TranslationMtx( v );

	fVal = 0.0f;

	if( fTime < 2.0f )
	{	
		m_piqFade->SetColor( 0xffffff );
		fVal = ( 2.0f - fTime )*128.0f;
	}

	if( fTime > 16.0f )
	{	
		m_piqFade->SetColor( 0x0 );
		fVal = ( fTime - 16.0f )*255.0f;
	}

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

	m_piqFade->SetAlpha( (DWORD)fVal );

	return TRUE;
}

BOOL CMeteorAproaching::RenderEfx()
{
	m_pDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0x0, 01.0f, 0 );
	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	m_pDevice->SetRenderState( D3DRS_WRAP0, D3DWRAP_U|D3DWRAP_V );
	
	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
	m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

	m_pDevice->SetTexture( 0, GetTexture( "deepspace.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 0.5f ) );
	m_mSphere.Render( m_pDevice );
	

	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
	m_pDevice->SetRenderState( D3DRS_AMBIENT, 0x505050 );
	
	D3DLIGHT8				d3dlit;
	D3DMATERIAL8			d3dmat;

	ZeroMemory( &d3dlit, sizeof(D3DLIGHT8) );
	ZeroMemory( &d3dmat, sizeof(D3DMATERIAL8) );

	d3dlit.Type = D3DLIGHT_POINT;
	d3dlit.Attenuation0 = 1.0f;
	d3dlit.Position.x = 0.0f;
	d3dlit.Position.y = 0.0f;
	d3dlit.Position.z = 0.0f;
	d3dlit.Diffuse.r = 1.0f;
	d3dlit.Diffuse.g = 0.8f;
	d3dlit.Diffuse.b = 0.5f;
	d3dlit.Range = 1000.0f;

	d3dmat.Ambient.r = 1.0f;
	d3dmat.Ambient.g = 1.0f;
	d3dmat.Ambient.b = 1.0f;
	d3dmat.Diffuse.a = 0.05f;
	d3dmat.Diffuse.r = 1.0f;
	d3dmat.Diffuse.g = 1.0f;
	d3dmat.Diffuse.b = 1.0f;

	m_pDevice->SetMaterial( &d3dmat );
	m_pDevice->SetLight( 0, &d3dlit );
	m_pDevice->LightEnable( 0, TRUE );	

	m_pDevice->SetTexture( 0, GetTexture( "youngearth.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, m_mtxEarth );
	m_mSphere.Render( m_pDevice );

	m_pDevice->SetTexture( 0, GetTexture( "moon.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, m_mtxMoon );
	m_mSphere.Render( m_pDevice );

	m_pDevice->SetRenderState( D3DRS_AMBIENT, 0xffffff );
	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->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );

	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 1.05f )*m_mtxEarth );
	m_mSphere.Render( m_pDevice );

	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 1.10f )*m_mtxEarth );	
	m_mSphere.Render( m_pDevice );
	
	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );
	m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );	

	m_pDevice->SetRenderState( D3DRS_WRAP0, FALSE );

	m_pDevice->SetTexture( 0, GetTexture( "sunflare.jpg" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, IdentMtx() );
	m_pDevice->SetVertexShader( FVF_LVERTEX );
	m_pDevice->SetStreamSource( 0, m_pvbFlare, sizeof(LVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 );

	m_pDevice->SetTexture( 0, m_ptextLens[3] );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 6, 26 );
	m_pDevice->SetTexture( 0, m_ptextLens[2] );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 84, 6 );
	m_pDevice->SetTexture( 0, m_ptextLens[0] );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 102, 2 );
	m_pDevice->SetTexture( 0, m_ptextLens[1] );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 108, 2 );		

	m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );	

	m_pDevice->SetTexture( 0, GetTexture( "smoke1.bmp" ) );
	m_pDevice->SetTransform( D3DTS_WORLD, m_mtxMeteor );
	m_pDevice->SetVertexShader( FVF_PARTICLEVERTEX );
	m_pDevice->SetStreamSource( 0, m_pvbMeteorParticles, sizeof(PARTICLEVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 100 );

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

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

	m_psMeteorSystem.Render( m_pDevice );
	m_psMeteorSystem2.Render( m_pDevice );

	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 );
	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );	
	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );

	return TRUE;
}
