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

#include "stdafx.h"
#include "Simulator.h"
#include "SDirectXTread.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

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

SDirectXTread::SDirectXTread()
{
	// some defaults
	TreadInfo.Length = 190;
	TreadInfo.Width = 50;
	TreadInfo.Radius = 20;
	TreadInfo.NumofTreads = 40; 
	TreadInfo.CenterPoint.x = 0;
	TreadInfo.CenterPoint.y = 0;
	TreadInfo.CenterPoint.z = 0; 

	m_pd3dDevice = NULL;
	g_pTreadVB = NULL;

	Tread.SeedLocation = Top;
	Tread.SeedDistance = 0;
	Tread.SeedAngle = 0;

}

SDirectXTread::~SDirectXTread()
{
	if (g_pTreadVB != NULL)
	    g_pTreadVB->Release();
}

/************************************************************************
* void Render()
*
* PURPOSE
* Renders the tread
*************************************************************************/
void SDirectXTread::Render()
{
	if ((m_pd3dDevice != NULL) &&	(g_pTreadVB != NULL))
	{
		// first move the world model apropraitly
		 float NScaling = 0.0001f; // tread specified in mm
		 float IScaling = 1000*NScaling;		 

        D3DXMATRIX matrixWorld,Rotation1,Translation,Scaling;

        D3DXMatrixIdentity(&Rotation1); 

        D3DXMatrixIdentity(&Translation);
		D3DXMatrixIdentity(&Scaling);
        D3DXMatrixScaling(&Scaling,NScaling,NScaling,NScaling);
		
        D3DXMatrixRotationY(&Rotation1,(TreadInfo.Angle)*PI/180);
	
        D3DXMatrixTranslation(&Translation,TreadInfo.CenterPoint.x/IScaling,TreadInfo.CenterPoint.y,TreadInfo.CenterPoint.z/IScaling); 
        matrixWorld =Rotation1*Translation*Scaling;

	    m_pd3dDevice->SetTransform( D3DTS_WORLD, &matrixWorld );

		// now render them
        m_pd3dDevice->SetStreamSource( 0, g_pTreadVB, sizeof(VERTEX) );
	    m_pd3dDevice->SetVertexShader( VERTEX_TYPE );

	  //  m_pd3dDevice->SetTexture(0,NULL);	// No Texture

    //	m_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);	// Wireframe for dubugging
        m_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
	    
	    m_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);	// Enable Z buffering

	    // Draw the Triangles
	    m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, TreadInfo.NumofTreads*2);
	}
}

/************************************************************************
* void Update(float Distance, D3DVECTOR CenterPoint, float Angle)
*
* PURPOSE
* Updates the treads positions and tread placement prior to a render
*************************************************************************/
void SDirectXTread::Update(float Distance, D3DVECTOR CenterPoint, float Angle)
{
	if ((m_pd3dDevice != NULL) &&	(g_pTreadVB != NULL))
	{
		TreadInfo.CenterPoint = CenterPoint;
		TreadInfo.Angle = Angle;

		// update the seed point;
		Tread = MoveLink(Tread,Distance);

		int VertexCount = 0;

		 VERTEX* pVertices; // pointer for traversal of VB

		// lock the buffer
		if( FAILED( g_pTreadVB->Lock(0, 0, (BYTE**)&pVertices, 0 ) ) )
		{
			return;
		}

		VERTEX* pCurrVertex = pVertices;

		// form vertex buffer

		BOOL Alt = false;

		// do the seed link
		FormLink(pCurrVertex,Tread,Alt);
		pCurrVertex = pCurrVertex + 6;
		VertexCount += 2;

		if (Alt)
			Alt = false;
		else
			Alt = true;

		TREAD tTread = Tread;

		// now do every other link
		for (int ix = 1; ix < TreadInfo.NumofTreads; ix++)
		{
			tTread = MoveLink(tTread,TreadInfo.LinkLength);
			FormLink(pCurrVertex,tTread,Alt);
			pCurrVertex = pCurrVertex + 6;
			VertexCount += 2;
					if (Alt)
			Alt = false;
		else
			Alt = true;
		}

		g_pTreadVB->Unlock();
	}
}

/************************************************************************
* TREAD MoveLink(TREAD TreadIn, float Distance)
*
* PURPOSE
* Moves the a point along the treads given the movement input distance.  This
* Function could work recursivly so if the point moves along more then one 
* Segment and the same time.
*************************************************************************/
SDirectXTread::TREAD SDirectXTread::MoveLink(SDirectXTread::TREAD TreadIn, float Distance)
{
	TREAD tReturn;
	tReturn.SeedAngle = TreadIn.SeedAngle;
	tReturn.SeedDistance = TreadIn.SeedDistance;
	tReturn.SeedLocation = TreadIn.SeedLocation;

	if ((TreadIn.SeedLocation == Top) || (TreadIn.SeedLocation == Bottom))
	{
		float NewDistance = TreadIn.SeedDistance + Distance;

		if (NewDistance < 0)
		{
			// backup up into one of the radial portions
			// find remianing distance to travel
			NewDistance = -Distance - TreadIn.SeedDistance;

			if (TreadIn.SeedLocation == Top)
			{
				tReturn.SeedLocation = Back;
				tReturn.SeedDistance = 0;
				tReturn.SeedAngle = 180.0f - (NewDistance/(2*(float)PI*TreadInfo.Radius)*360.0f);

				if (tReturn.SeedAngle < 0)
				{
					tReturn.SeedAngle = 0;
					tReturn.SeedLocation = Bottom;
					// call function again, because 180 exceed with this large distance.
					tReturn = MoveLink(tReturn, NewDistance - (float)PI*TreadInfo.Radius);
				}
			}
			else if (TreadIn.SeedLocation == Bottom)
			{
				tReturn.SeedLocation = Front;
				tReturn.SeedDistance = 0;
				tReturn.SeedAngle = 180.0f - (NewDistance/(2*(float)PI*TreadInfo.Radius)*360.0f);

				if (tReturn.SeedAngle < 0)
				{
					tReturn.SeedAngle = 0;
					tReturn.SeedLocation = Top;
					// call function again, because 180 exceed with this large distance.
					tReturn = MoveLink(tReturn, NewDistance - (float)PI*TreadInfo.Radius);
				}
			}
		}
		else if (NewDistance > TreadInfo.Length)
		{
			// go to one of the radial portions
			// find remianing distance to travel
			NewDistance = (Distance + TreadIn.SeedDistance) - TreadInfo.Length;

			if (TreadIn.SeedLocation == Top)
			{
				tReturn.SeedLocation = Front;
				tReturn.SeedDistance = 0;
				tReturn.SeedAngle = (360.0f*NewDistance/(2*(float)PI*TreadInfo.Radius));

				if (TreadIn.SeedAngle > 180.0f)
				{
					tReturn.SeedAngle = 0;
					tReturn.SeedLocation = Bottom;
					// call function again, because 180 exceed with this large distance.
					tReturn = MoveLink(tReturn, NewDistance - (float)PI*TreadInfo.Radius);
				}
			}
			else if (TreadIn.SeedLocation == Bottom)
			{
				tReturn.SeedLocation = Back;
				tReturn.SeedDistance = 0;
				tReturn.SeedAngle = (360.0f*NewDistance/(2*(float)PI*TreadInfo.Radius));

				if (tReturn.SeedAngle > 180.0f)
				{
					tReturn.SeedAngle = 0;
					tReturn.SeedLocation = Top;
					// call function again, because 180 exceed with this large distance.
					tReturn = MoveLink(tReturn, NewDistance - (float)PI*TreadInfo.Radius);
				}
			}
		}
		else
		{
			tReturn.SeedDistance = NewDistance;
		}
		}
	else // if ((TreadIn.SeedLocation == Front) || (TreadIn.SeedLocation == Back))
	{
		float NewAngle = TreadIn.SeedAngle + (360.0f*Distance/(2*(float)PI*TreadInfo.Radius));

		if (NewAngle < 0.0f)
		{
			if (TreadIn.SeedLocation == Front)
			{
				tReturn.SeedLocation = Top;
				tReturn.SeedAngle = 0;
				// get the new position
				tReturn.SeedDistance = TreadInfo.Length + Distance + TreadIn.SeedAngle*(2*(float)PI*TreadInfo.Radius)/360.0f;

				if (tReturn.SeedDistance < 0.0f)
				{
					tReturn.SeedAngle = 180.0f;
					tReturn.SeedLocation = Back;
					tReturn = MoveLink(tReturn, tReturn.SeedDistance);
				}
			}
			else // if (TreadIn.SeedLocation == Back)
			{
				tReturn.SeedLocation = Bottom;
				tReturn.SeedAngle = 0;
				// get the new position
				tReturn.SeedDistance = TreadInfo.Length + Distance + TreadIn.SeedAngle*(2*(float)PI*TreadInfo.Radius)/360.0f;

				if (TreadIn.SeedDistance < 0.0f)
				{
					tReturn.SeedAngle = 180.0f;
					tReturn.SeedLocation = Front;
					tReturn = MoveLink(tReturn, tReturn.SeedDistance);
				}
			}
		}
		else if (NewAngle > 180.0f)
		{
			if (TreadIn.SeedLocation == Front)
			{
				tReturn.SeedLocation = Bottom;
				tReturn.SeedAngle = 0;
				// get the new position
				tReturn.SeedDistance = Distance - (180.0f - TreadIn.SeedAngle)*(2*(float)PI*TreadInfo.Radius)/360.0f;

				if (tReturn.SeedDistance > TreadInfo.Length)
				{
					tReturn.SeedAngle = 0.0f;
					tReturn.SeedLocation = Back;
					tReturn = MoveLink(tReturn, tReturn.SeedDistance);
				}
			}
			if (TreadIn.SeedLocation == Back)
			{
				tReturn.SeedLocation = Top;
				tReturn.SeedAngle = 0;
				// get the new position
				tReturn.SeedDistance = Distance - (180.0f - TreadIn.SeedAngle)*(2*(float)PI*TreadInfo.Radius)/360.0f;

				if (tReturn.SeedDistance > TreadInfo.Length)
				{
					tReturn.SeedAngle = 0.0f;
					tReturn.SeedLocation = Front;
					tReturn = MoveLink(tReturn, tReturn.SeedDistance);
				}
			}
		}
		else
		{
			tReturn.SeedAngle = NewAngle;
		}
	}

	return tReturn;
		
}

/************************************************************************
* Create(float Radius, float NumofTreads, float Width, float Length, LPDIRECT3DDEVICE8 m_pd3dDevice)
*
* PURPOSE
* Creates the tread given all the information and a device
*************************************************************************/
void SDirectXTread::Create(SDirectXTread::TREADINFO tTreadInfo, LPDIRECT3DDEVICE8 tm_pd3dDevice)
{
	TreadInfo = tTreadInfo;
	m_pd3dDevice = tm_pd3dDevice;

	int NumberofVertices = (int)(TreadInfo.NumofTreads*6);

	TreadInfo.LinkLength = (TreadInfo.Length*2 + TreadInfo.Radius*2*PI)/TreadInfo.NumofTreads;

	 if( FAILED( m_pd3dDevice->CreateVertexBuffer( NumberofVertices*sizeof(VERTEX),
												   D3DUSAGE_DYNAMIC ,
												   VERTEX_TYPE,
												   D3DPOOL_DEFAULT,
												   &g_pTreadVB 
												   ) )) 
                                                   AfxMessageBox("Failed Tread Vertex Buffer");
}

/************************************************************************
* FormLink(VERTEX *pVertex, TREAD TreadIn)
*
* PURPOSE
* Used for rendering.  Takes a link information and coverts it to tread vertices
*************************************************************************/
void SDirectXTread::FormLink(VERTEX *pVertex, SDirectXTread::TREAD TreadIn, BOOL Alt)
{
//	DWORD Color1 = 0xff202020;
//	DWORD Color2 = 0xff604080;

	DWORD Color1 = 0xffffffff;
	DWORD Color2 = 0xffffffff;

	if (Alt)
	{
		//Color1 = 0xffe50000;
		//Color2 = 0xff34e820;

		Color1 = 0xffffffff;
		Color2 = 0xffffffff;
	}

	
	if ((TreadIn.SeedLocation == Top) || (TreadIn.SeedLocation == Bottom))
	{
		float Height;
		FRect Rect;

		if (TreadIn.SeedLocation == Top)
		{
			Height = TreadInfo.Radius*2;
			Rect.top = TreadInfo.Width/2.0f;
			Rect.bottom = Rect.top - TreadInfo.Width;
			Rect.left = TreadInfo.LinkLength/2.0f + TreadIn.SeedDistance - TreadInfo.Length/2.0f;
			Rect.right = Rect.left - TreadInfo.LinkLength;
		}
		else // if (TreadIn.SeedLocation == Bottom)
		{
			// same as top except reverse rectange and alter height
			Height = 0.0f;
			Rect.bottom = -TreadInfo.Width/2.0f;
			Rect.top = Rect.bottom + TreadInfo.Width;
			Rect.left = -TreadInfo.LinkLength/2.0f - TreadIn.SeedDistance + TreadInfo.Length/2.0f;
			Rect.right = Rect.left + TreadInfo.LinkLength;
		}

		pVertex[0].position.x = Rect.left;
		pVertex[0].position.y = Height;
		pVertex[0].position.z = Rect.top;
		pVertex[0].color = Color1;
		pVertex[0].tu = 1.0f;
		pVertex[0].tv = 1.0f;

		pVertex[1].position.x = Rect.right;
		pVertex[1].position.y = Height;
		pVertex[1].position.z = Rect.top;
		pVertex[1].color = Color2;
		pVertex[1].tu = 0.0f;
		pVertex[1].tv = 1.0f;

		pVertex[2].position.x = Rect.right;
		pVertex[2].position.y = Height;
		pVertex[2].position.z = Rect.bottom;
		pVertex[2].color = Color2;
		pVertex[2].tu = 0.0f;
		pVertex[2].tv = 0.0f;

		pVertex[3].position.x = Rect.left;
		pVertex[3].position.y = Height;
		pVertex[3].position.z = Rect.top;
		pVertex[3].color = Color1;
		pVertex[3].tu = 1.0f;
		pVertex[3].tv = 1.0f;

		pVertex[4].position.x = Rect.left;
		pVertex[4].position.y = Height;
		pVertex[4].position.z = Rect.bottom;
		pVertex[4].color = Color1;
		pVertex[4].tu = 1.0f;
		pVertex[4].tv = 0.0f;


		pVertex[5].position.x = Rect.right;
		pVertex[5].position.y = Height;
		pVertex[5].position.z = Rect.bottom;
		pVertex[5].color = Color2;
		pVertex[5].tu = 0.0f;
		pVertex[5].tv = 0.0f;

	}
	else // if ((TreadIn.SeedLocation == Front) || (TreadIn.SeedLocation == Back))
	{
		float X;
		float Y;
		float Tangent;

		if (TreadIn.SeedLocation == Front)
		{
			// in the radial area, first find x and y;
			X = -TreadInfo.Radius*cosf((TreadIn.SeedAngle + 90)*(float)PI/180.0f) + TreadInfo.Length/2.0f;
			Y = TreadInfo.Radius*sinf((TreadIn.SeedAngle + 90)*(float)PI/180.0f) + TreadInfo.Radius;

			Tangent = TreadIn.SeedAngle + 180.0f;
		}
		else
		{
			X = -TreadInfo.Radius*cosf((TreadIn.SeedAngle - 90)*(float)PI/180.0f) - TreadInfo.Length/2.0f;
			Y = TreadInfo.Radius*sinf((TreadIn.SeedAngle - 90)*(float)PI/180.0f) + TreadInfo.Radius;

			Tangent = TreadIn.SeedAngle;
		}


		D3DVECTOR Point1,Point2,Point3,Point4;

		Point1.x = Point2.x = X - TreadInfo.LinkLength*cosf(Tangent*(float)PI/180.0f)/2.0f;
		Point3.x = Point4.x = X + TreadInfo.LinkLength*cosf(Tangent*(float)PI/180.0f)/2.0f;

		Point1.z = Point4.z = TreadInfo.Width/2.0f;
		Point2.z = Point3.z = -TreadInfo.Width/2.0f;

		Point1.y = Point2.y = Y + TreadInfo.LinkLength*sinf(Tangent*(float)PI/180.0f)/2.0f; 
		Point3.y = Point4.y = Y - TreadInfo.LinkLength*sinf(Tangent*(float)PI/180.0f)/2.0f; 

		pVertex[0].position.x = Point1.x;
		pVertex[0].position.y = Point1.y;
		pVertex[0].position.z = Point1.z;
		pVertex[0].color = Color1;
		pVertex[0].tu = 1.0f;
		pVertex[0].tv = 1.0f;

		pVertex[1].position.x = Point2.x;
		pVertex[1].position.y = Point2.y;
		pVertex[1].position.z = Point2.z;
		pVertex[1].color = Color1;
		pVertex[1].tu = 1.0f;
		pVertex[1].tv = 0.0f;

		pVertex[2].position.x = Point3.x;
		pVertex[2].position.y = Point3.y;
		pVertex[2].position.z = Point3.z;
		pVertex[2].color = Color2;
		pVertex[2].tu = 0.0f;
		pVertex[2].tv = 0.0f;

		pVertex[3].position.x = Point1.x;
		pVertex[3].position.y = Point1.y;
		pVertex[3].position.z = Point1.z;
		pVertex[3].color = Color1;
		pVertex[3].tu = 1.0f;
		pVertex[3].tv = 1.0f;

		pVertex[4].position.x = Point4.x;
		pVertex[4].position.y = Point4.y;
		pVertex[4].position.z = Point4.z;
		pVertex[4].color = Color2;
		pVertex[4].tu = 0.0f;
		pVertex[4].tv = 1.0f;


		pVertex[5].position.x = Point3.x;
		pVertex[5].position.y = Point3.y;
		pVertex[5].position.z = Point3.z;
		pVertex[5].color = Color2;
		pVertex[5].tu = 0.0f;
		pVertex[5].tv = 0.0f;
	}

}
