// Tunel.cpp: implementation of the Tunel class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Tunnel.h"

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

CTunnel::CTunnel( PDIRECT3DDEVICE8 pDevice ) : CEfx(  pDevice ), m_blobsSystem( 22 )
{	
	m_pvbCells = NULL;
	m_pibCells = NULL;
	m_pvbWhiteCells = NULL;

	if( !m_mRedCell.Load( m_pDevice, "data\\objects\\redcell.smf" ) )
		throw CSystemException( DEMO_EXCEPTION_UNABLETOREAD, "unable to load redcell.smf" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 100*m_mRedCell.GetVertexCount()*sizeof(D3DVECTOR), D3DUSAGE_DYNAMIC, D3DFVF_XYZ, D3DPOOL_DEFAULT, &m_pvbCells ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create cells vertex buffer" );

	if( FAILED( m_pDevice->CreateIndexBuffer( 100*3*m_mRedCell.GetFacesCount()*sizeof(WORD), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pibCells ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create cells index buffer" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 100*sizeof(D3DVECTOR), 0, D3DFVF_XYZ, D3DPOOL_DEFAULT, &m_pvbWhiteCells ) ) )
		throw CSystemException( DEMO_EXCEPTION_D3DERROR, "unable to create white cells vertex buffer" );

	m_piqFade = new CImageQuad( m_pDevice );
	m_piqFade->SetColor( 0xff0000 );

	m_piqAuthor = new CImageQuad( m_pDevice, 0, 0, 1, 1 );
	m_piqFade->SetColor( 0xffffff );
	m_piqFade->SetAlpha( 0x0 );	

	m_ptextAuthor = NULL;

	LPBYTE					tmp;
	LPWORD					pIndices;
	LPWORD					pDestIndices;

	m_mRedCell.LockMesh( &tmp, (LPBYTE*)&pIndices );
	m_pibCells->Lock( 0, 0, (LPBYTE*)&pDestIndices, 0 );

	for( DWORD i = 0 ; i < 100 ; i++ )
	{	
		for( DWORD j = 0 ; j < 3*m_mRedCell.GetFacesCount() ; j++ )		
			*(pDestIndices++) = pIndices[j] + (WORD)(i*m_mRedCell.GetVertexCount());		

		m_pfCells[i] = (FLOAT)i;
		m_pvCells[i] = 1.5f*RAND()*Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) );
		m_pfWhiteCells[i] = (FLOAT)i;
		m_pvWhiteCells[i] = 1.5f*RAND()*Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) );
	}

	m_mRedCell.UnlockMesh();
	m_pibCells->Unlock();

	try
	{
		LoadTexture( "vein.jpg" );
		LoadTexture( "part.jpg" );
		LoadTexture( "red.jpg" );
		LoadTexture( "acid.jpg" );
		LoadTexture( "beton.jpg" );
		LoadTexture( "druglord.jpg" );
		LoadTexture( "juve.jpg" );
	}
	catch( CTextureException )
	{
		throw CSystemException( DEMO_EXCEPTION_FILENOTFOUND, "unable to load textures" );
	}
	
	srand( 931317 );

	spline[0].m_vPos = 20.0f*he3d_CVector( 0, 0, 0 );
	spline[0].m_fT = 0.0f;

	for( i = 1 ; i < KEYSCOUNT ; i++ )
	{
		he3d_CVector		v = Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) );

		spline[i].m_vPos = (10.0f + 10.0f*RAND())*v + spline[i-1].m_vPos;
		spline[i].m_fT = spline[i-1].m_fT + 10.0f*Length( spline[i].m_vPos - spline[i-1].m_vPos );
	}		

	FLOAT				delta1, delta2;	
	he3d_CVector		v1, v2;	

	for( i = 0 ; i < KEYSCOUNT ; i++ )
	{		
		delta1 = 0.5f;
		delta2 = 0.5f;

		if( i != KEYSCOUNT - 1 )		
			v1 = spline[i+1].m_vPos - spline[i].m_vPos;	

		if( i != 0 )		
			v2 = spline[i].m_vPos - spline[i-1].m_vPos;
		
		if( i == KEYSCOUNT )
			v1 = v2;

		if( i == 0 )
			v2 = v1;

		if( i != 0 && i != KEYSCOUNT )
		{
			delta1 = ( spline[i+1].m_fT - spline[i].m_fT )/( spline[i+1].m_fT - spline[i-1].m_fT );
			delta2 = ( spline[i].m_fT - spline[i-1].m_fT )/( spline[i+1].m_fT - spline[i-1].m_fT );
		}		

		spline[i].t0 = delta1*( v1 + v2 );		
		spline[i].t1 = delta2*( v1 + v2 );
	}

	m_blobsSystem.Initialize( m_pDevice );

	for( i = 0 ; i < BLOBSCOUNT ; i++ )
	{
		m_blobs[i].m_fDensity = 1.0f;
		m_blobs[i].m_fRadius = 2.0f;
		m_blobs[i].m_vPos = CalculateVector( (FLOAT)i*10.0f );

		m_blobsSystem.AddBlob( &m_blobs[i] );
	}

	m_blobsSystem.CalculateDensity();

	m_fCurrentT = 0.0f;		

	for( i = 0 ; i < 100 ; i++ )
	{					
		m_pfCells[i] = (FLOAT)i;
		m_pvCells[i] = 1.5f*RAND()*Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) ) + CalculateVector( m_pfCells[i] );
		m_pfWhiteCells[i] = 2.0f*(FLOAT)i;
		m_pvWhiteCells[i] = 1.5f*RAND()*Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) ) + CalculateVector( m_pfWhiteCells[i] );
	}
}

CTunnel::~CTunnel()
{
	if( m_pvbCells )
		m_pvbCells->Release();

	if( m_pibCells )
		m_pibCells->Release();

	if( m_pvbWhiteCells )
		m_pvbWhiteCells->Release();

	CEfx::~CEfx();
}

he3d_CVector CTunnel::CalculateVector( FLOAT fT )
{		
	if( spline[0].m_fT > fT )
		return spline[0].m_vPos;

	if( spline[KEYSCOUNT-1].m_fT < fT )
		return spline[KEYSCOUNT].m_vPos;

	for( DWORD i = 0 ; i < KEYSCOUNT - 1  && spline[i+1].m_fT < fT ; i++ );

	FLOAT					t, t2, t3;
	FLOAT					h[4];

	t = (fT - spline[i].m_fT)/(spline[i+1].m_fT - spline[i].m_fT);	

	t2 = t*t;
	t3 = t2*t;	

	h[0] = 2*t3 - 3*t2 + 1;
	h[1] = -2*t3 + 3*t2;
	h[2] = t3 - 2*t2 + t;
	h[3] = t3 - t2;

	return h[0]*spline[i].m_vPos + h[1]*spline[i+1].m_vPos + h[2]*spline[i].t0 + h[3]*spline[i+1].t1;
}

he3d_CVector CTunnel::CalculateDeriv( FLOAT fT )
{	
	if( spline[0].m_fT > fT )
		return spline[0].m_vPos;

	if( spline[KEYSCOUNT-1].m_fT < fT )
		return spline[KEYSCOUNT].m_vPos;

	for( DWORD i = 0 ; i < KEYSCOUNT - 1  && spline[i+1].m_fT < fT ; i++ );

	FLOAT					t, t2;
	FLOAT					h[4];

	t = (fT - spline[i].m_fT)/(spline[i+1].m_fT - spline[i].m_fT);	

	t2 = t*t;		

	h[0] = 6*t2 - 6*t;
	h[1] = -6*t2 + 6*t;
	h[2] = 3*t2 - 4*t + 1;
	h[3] = 3*t2 - 2*t;

	return Normalize( h[0]*spline[i].m_vPos + h[1]*spline[i+1].m_vPos + h[2]*spline[i].t0 + h[3]*spline[i+1].t1 );
}

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

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

BOOL CTunnel::UpdateFrame( FLOAT fTime )
{
	m_fMove = fTime;

	m_fCurrentT = fTime*50.0f;

	for( DWORD i = 0 ; i < BLOBSCOUNT ; i++ )
	{
		m_blobs[i].m_fRadius = 2.0f + 0.5f*(sinf( 2.0f*fTime + 0.5f*(FLOAT)i ) + 1.0f);		
		m_blobs[i].m_vPos = CalculateVector( (FLOAT)i*10.0f + fTime*50.0f );		
	}	

	m_blobsSystem.CalculateDensity();

	m_mtxCamera = CameraMtx( CalculateVector( m_fCurrentT ), CalculateVector( m_fCurrentT + 80.0f ), 30.0f*fTime );

	PVERTEX					pVerts;
	D3DVECTOR*				pDestVerts;
	D3DVECTOR*				pDestVerts2;
	LPBYTE					tmp;	

	he3d_CMatrix			mtx;

	m_mRedCell.LockMesh( (LPBYTE*)&pVerts, &tmp );
	m_pvbCells->Lock( 0, 0, (LPBYTE*)&pDestVerts, 0 );
	m_pvbWhiteCells->Lock( 0, 0, (LPBYTE*)&pDestVerts2, 0 );

	for( i = 0 ; i < 100 ; i++ )
	{		
		if( m_pfCells[i] < m_fCurrentT )
		{		
			m_pfCells[i] = 2.0f*m_fCurrentT + 100.0f - m_pfCells[i];
			m_pvCells[i] = 1.5f*RAND()*Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) ) + CalculateVector( m_pfCells[i] );
		}

		if( m_pfWhiteCells[i] < m_fCurrentT )
		{
			m_pfWhiteCells[i] = 2.0f*m_fCurrentT + 100.0f - m_pfWhiteCells[i];
			m_pvWhiteCells[i] = 1.5f*RAND()*Normalize( he3d_CVector( SIGNEDRAND(), SIGNEDRAND(), SIGNEDRAND() ) ) + CalculateVector( m_pfWhiteCells[i] );
		}

		switch( i&0x03 )
		{
			case 0x00:
				mtx = ScaleMtx( 0.04f, 0.04f, 0.06f )*RotationMtx( (FLOAT)i + 2.0f*fTime, -(FLOAT)i - 2.0f*fTime, (FLOAT)i + fTime )*TranslationMtx( m_pvCells[i] );
				break;
		
			case 0x01:
				mtx = ScaleMtx( 0.03f, 0.03f, 0.05f )*RotationMtx( -(FLOAT)i + 2.0f*fTime, (FLOAT)i + 1.5f*fTime, -(FLOAT)i - fTime )*TranslationMtx( m_pvCells[i] );
				break;

			case 0x02:
				mtx = ScaleMtx( 0.04f, 0.03f, 0.06f )*RotationMtx( -(FLOAT)i - fTime, -(FLOAT)i - 0.5f*fTime, (FLOAT)i + 2.0f*fTime )*TranslationMtx( m_pvCells[i] );
				break;

			default:
				mtx = ScaleMtx( 0.04f, 0.04f, 0.06f )*RotationMtx(  (FLOAT)i + fTime,  (FLOAT)i + fTime, -(FLOAT)i - 2.0f*fTime )*TranslationMtx( m_pvCells[i] );
				break;
		}

		for( DWORD j = 0 ; j < m_mRedCell.GetVertexCount() ; j++, pDestVerts++ )		
			*pDestVerts = pVerts[j].m_vPos*mtx;					

		pDestVerts2[i] = m_pvWhiteCells[i];
	}

	m_mRedCell.UnlockMesh();
	m_pvbCells->Unlock();
	m_pvbWhiteCells->Unlock();

	FLOAT					fAlpha = 0.0f;// 3.0f - fTime;

	m_bRenderFade = FALSE;

	if( fTime < 2.0f )
	{
		m_bRenderFade = TRUE;
		m_piqFade->SetColor( 0xff0000 );
		fAlpha = ( 2.0f - fTime )*128.0f;
	}

	if( fTime > 19.0f )
	{
		m_bRenderFade = TRUE;
		m_piqFade->SetColor( 0x0 );
		fAlpha = ( fTime - 19.0f )*128.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 >= 2.5f && fTime <= 5.5f )
	{
		m_piqAuthor->Resize( 400.0f - 111.0f, 350.0f - 70.0f, 400.0f + 111.0f, 350.0f + 70.0f );
		m_ptextAuthor = GetTexture( "acid.jpg" );
		fAlpha = 255.0f*sinf( (fTime - 2.5f)*H_PI/3.0f );
	}

	if( fTime >= 6.5f && fTime <= 9.5f )
	{
		m_piqAuthor->Resize( 400.0f - 141.0f, 350.0f - 93.0f, 400.0f + 141.0f, 350.0f + 93.0f );
		m_ptextAuthor = GetTexture( "beton.jpg" );
		fAlpha = 255.0f*sinf( (fTime - 6.5f)*H_PI/3.0f );
	}

	if( fTime >= 10.5f && fTime <= 13.5f )
	{
		m_piqAuthor->Resize( 400.0f - 180.0f, 350.0f - 111.0f, 400.0f + 180.0f, 350.0f + 111.0f );
		m_ptextAuthor = GetTexture( "druglord.jpg" );
		fAlpha = 255.0f*sinf( (fTime - 10.5f)*H_PI/3.0f );
	}

	if( fTime >= 14.5f && fTime <= 17.5f )
	{
		m_piqAuthor->Resize( 400.0f - 107.0f, 350.0f - 74.0f, 400.0f + 107.0f, 350.0f + 70.4f );
		m_ptextAuthor = GetTexture( "juve.jpg" );
		fAlpha = 255.0f*sinf( (fTime - 14.5f)*H_PI/3.0f );
	}

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

	m_piqAuthor->SetAlpha( (DWORD)fAlpha );

	return TRUE;
}

BOOL CTunnel::RenderEfx()
{
	m_pDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0x0ff, 1.0f, 0 );
	
	D3DLIGHT8				lit;
	ZeroMemory( &lit, sizeof(D3DLIGHT8) );

	lit.Type = D3DLIGHT_POINT;
	lit.Diffuse.r = 1.0f;
	lit.Diffuse.g = 1.0f;
	lit.Diffuse.b = 1.0f;
	lit.Position = CalculateVector( m_fCurrentT );
	lit.Attenuation0 = 1.0f;
	lit.Range = 100.0f;

	D3DMATERIAL8			mat;
	ZeroMemory( &mat, sizeof(D3DMATERIAL8) );

	mat.Diffuse.r = 1.0f;
	mat.Diffuse.g = 1.0f;
	mat.Diffuse.b = 1.0f;
	mat.Ambient.r = 1.0f;
	mat.Ambient.g = 1.0f;
	mat.Ambient.b = 1.0f;

	m_pDevice->SetMaterial( &mat );
	m_pDevice->SetLight( 0, &lit );
	m_pDevice->LightEnable( 0, TRUE );

	m_pDevice->SetTexture( 0, GetTexture( "vein.jpg" ) );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL );
	m_pDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
	m_pDevice->SetRenderState( D3DRS_AMBIENT, 0x808080 );
	m_pDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_USEW );
	m_pDevice->SetTransform( D3DTS_VIEW, m_mtxCamera );		
	m_pDevice->SetTransform( D3DTS_WORLD, IdentMtx() );
	m_pDevice->SetTransform( D3DTS_TEXTURE0, TranslationMtx( 1.0f, 1.0f, 1.0f )*ScaleMtx( 0.5f ) );	

	m_blobsSystem.Render( m_pDevice );

	m_pDevice->SetTexture( 0, NULL );
	
	mat.Ambient.r = 0.3f;
	mat.Ambient.g = 0.0f;
	mat.Ambient.b = 0.0f;

	m_pDevice->SetMaterial( &mat );
	
	m_pDevice->LightEnable( 0, FALSE );
	m_pDevice->SetRenderState( D3DRS_AMBIENT, 0x805050 );
	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );

	m_pDevice->SetVertexShader( D3DFVF_XYZ );
	m_pDevice->SetStreamSource( 0, m_pvbCells, sizeof(D3DVECTOR) );
	m_pDevice->SetIndices( m_pibCells, 0 );
	m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 100*m_mRedCell.GetVertexCount(), 0, 100*m_mRedCell.GetFacesCount() );

	m_pDevice->SetTexture( 0, GetTexture( "part.jpg" ) );
	
	mat.Ambient.r = 0.5f;
	mat.Ambient.g = 0.5f;
	mat.Ambient.b = 0.5f;

	m_pDevice->SetMaterial( &mat );
	m_pDevice->SetRenderState( D3DRS_AMBIENT, 0x808080 );
	m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_POINTSIZE, FLT2DWORD( 50.0f ) );
	m_pDevice->SetRenderState( D3DRS_POINTSCALE_A, FLT2DWORD( 2.0f ) );
	m_pDevice->SetRenderState( D3DRS_POINTSCALE_B, FLT2DWORD( 1.0f ) );
	m_pDevice->SetStreamSource( 0, m_pvbWhiteCells, sizeof(D3DVECTOR) );
	m_pDevice->SetIndices( NULL, 0 );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 100 );

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

	m_pDevice->SetTexture( 0, NULL );	

	if( m_bRenderFade )
	{	
		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, m_ptextAuthor );
	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
	m_piqAuthor->Render( m_pDevice );
	m_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

	return TRUE;
}

