/*****************************************************************************
  Name :     Wheel 
  Author:	 Carlos Sarria
  Date :     April 1999 
  Platform : Dreamcast Set5 (CLX2 + SH4)
 
  Description : BumpMap demo.

  Copyright : 1999 by VideoLogic Limited. All rights reserved.
******************************************************************************/
#include <math.h>

#include "KMTools.h"
#include "KMShell.h"

/* 3D data */     
#include "Wheel.h"
 
/* Textures that have been loaded with Data.c */
extern const unsigned char Tire[];
extern const unsigned char TireBump[];
extern const unsigned char Logo[];
extern const unsigned char Reflect[];
extern const unsigned char Ground[];
extern const unsigned char GroundBump[];

/*************************************************/
/*	Globals                                      */
/*************************************************/

KMSURFACEDESC		Tex[256];				/* texture surface */
KMVERTEXCONTEXT		VertexContext[256];		/* Vertex context */
KMVERTEX_03			*KmVert[256];			/* Vertex pointers*/

/* Bumpmap data */
float *pBump[256];

/* SET4 / SET5 memory start*/
int MemPtr = 0x0c400000;

/* Camera data */
int ScreenWidth=640, ScreenHeight=480;

km_vector CameraFrom		= { 0.0f, 100.0f, -100.0f};
km_vector CameraTo			= { 0.0f, 0.0f, 0.0f};
km_vector LightDirection	= {1.0f, -1.0f, 1.0f};

/* Generic data */
unsigned nFrame = 500;

/* Wheel angles */
float fAng1 = 0.0f , fAng2 = -1.2f, fAng3 = 0.0f;

/* Camera distance */
float fDist = 75.0f;

/* Light angle */
float fLAng2 = -11.0;

/* Wheel movement */
float fDistX, fDistY, fWheelCount=0, fSpeed = 1.0f, fSincro = 0.040;

/**************************************************/
/* Prototypes                                     */
/**************************************************/
void	InitMeshes			(void);
void	InitMaterials		(void);
void	InitBumpmapMesh		(int Object);
void	CalculateGround		(void);

/************************************************************************/
/* KMShell functions:                                                   */
/*    KMInitApplication                                                 */
/*    KMQuitApplication                                                 */
/*    KMRenderScene                                                     */
/*    KMPadControl                                                      * 
/*                                                                      */
/* WARNING : These functions must be created always because are called  */
/*           by KMShell.											    */
/************************************************************************/
/*----------------------------------------------------------------------*
 *  Function Name   :  InitApplication                              
 *  Inputs          :  None                                               
 *  Outputs         :  None                                                 
 *  Returns         :                                              
 *  Globals Used    :                                                
 *  Description     :   KMInitApplication() is called by KMShell to enable
 *						user to initialise the application                                           
 *----------------------------------------------------------------------*/
void InitApplication (void)
{
	KMShellSetVideo	(FALSE		/* Trans PreSort   */
					 );

	KMShellSetBuffers(50, 		/* % Opaque        */
					  0,		/* % ModVol Opaque */
					  50,		/* % Translucent   */
					  0,		/* % ModVol Trans  */
					  0			/* % Punchthrough  */
					  );		

	KMABCTableInit (TRUE);

	InitMaterials ();
 
	InitMeshes();

	InitBumpmapMesh (M_PLATESBUMP);
	InitBumpmapMesh (M_TIREBUMP);
	InitBumpmapMesh (M_GROUNDBUMP);
	
	LightDirection[0] = cos(fLAng2);
	LightDirection[1] = -0.5f;
	LightDirection[2] = sin(fLAng2);

	CalculateGround ();
	
}
/*----------------------------------------------------------------------*
 *  Function Name   :                                   
 *  Inputs          :  None                                               
 *  Outputs         :  None                                                 
 *  Returns         :                                            
 *  Globals Used    :                                                 
 *  Description     :                                                  
 *----------------------------------------------------------------------*/
void QuitApplication (void)
{
	KMABCTableRelease();

	/* FREE TEXTURES */
	kmFreeTexture(&Tex[0]);
	kmFreeTexture(&Tex[1]);
	kmFreeTexture(&Tex[2]);
	kmFreeTexture(&Tex[3]);
	kmFreeTexture(&Tex[4]);
	kmFreeTexture(&Tex[5]);
	
}

/*----------------------------------------------------------------------*
 *  Function Name   :  KMRenderScene                                      
 *  Inputs          :  None                                               
 *  Outputs         :  None                                                 
 *  Returns         :  FALSE to finish                                               
 *  Globals Used    :  nFrame, CameraTo, CameraFrom, nPT                                                
 *  Description     :  Draws everything in the scene.                                                 
 *----------------------------------------------------------------------*/
int RenderScene (void)
{
MATRIX		Matrx;
register	i, j;

	/* Wheel booble */
	fAng3 = (float)sin(fWheelCount) * 0.05f;

	for(i=0; i<NUM_MESHES; i++)
	{
		KMMatrixReset	  (&Matrx);

		/* Rotation */
		if(i!=M_GROUND && i!=M_GROUNDBUMP && i!=M_SUPPORT)	KMMatrixRotate    (&Matrx,  fAng1, fAng2, fAng3);
		
		else if (i == M_SUPPORT) KMMatrixRotate    (&Matrx,  0.0f, fAng2, 0.0f);

		/* Translation (Zoom) */
		KMMatrixTranslate (&Matrx, 0.0f, -fDist, fDist);  
   
		/* Vertices transformation */
		KMTransTransformProjectPerspective (&Matrx, Mesh[i].nNumVertex, Mesh[i].pVertex, 
										KmVert[i], ScreenWidth, ScreenHeight,
										CameraFrom, CameraTo, 0.0f, 1.0f, 1000000.0f, 60.0f*(PI/180.0f));

		/* Smooth shading */
		if(i==M_RIM || i==M_SUPPORT)
		{
			KMLightSmoothShade (&Matrx, Mesh[i].nNumVertex, Mesh[i].pNormals, (KMVERTEX_03 *)KmVert[i],
								LightDirection, 0xFFFFFFFF, FALSE);
		}

	
		/* Environment map */
		if (i==M_PLATES || i==M_RIM || i==M_SUPPORT)
		{
			/* Reduce displacement to avoid 'crazy' env-map and add a horizontal displacement*/
			Matrx._41 = (fDistX+fDistY) * 0.2f; 
			Matrx._42 = 0.00f; 
			Matrx._43 = -5.00f;

			KMTransEnvironmentMap (&Matrx, Mesh[i].nNumVertex, Mesh[i].pNormals, 
											KmVert[i], 	CameraFrom, CameraTo, 0.0f, 
											1.0f, 1000000.0f, 30.0f*(PI/180.0f));
		}

		/* Calculation of smooth-shading and bump-map in one go */ 
		if (i==M_TIREBUMP)
		{
			KMBumpShade (&Matrx, Mesh[M_TIREBUMP].nNumVertex, pBump[M_TIREBUMP], Mesh[M_TIREBUMP].pNormals,
						KmVert[M_TIREBUMP], KmVert[M_TIRE],	LightDirection, BUMP_METHOD_TRANSLUCENT);
		}
	
		if (i==M_PLATESBUMP)
		{
  			KMBumpShade (&Matrx, Mesh[M_PLATESBUMP].nNumVertex, pBump[M_PLATESBUMP], Mesh[M_PLATESBUMP].pNormals,
						KmVert[M_PLATESBUMP], KmVert[M_PLATES],	LightDirection, BUMP_METHOD_HIGHLIGHT);
		}

		if (i==M_GROUNDBUMP)
		{
  			KMBumpShade (&Matrx, Mesh[M_GROUNDBUMP].nNumVertex, pBump[M_GROUNDBUMP], Mesh[M_GROUNDBUMP].pNormals,
						KmVert[M_GROUNDBUMP], NULL,	LightDirection, BUMP_METHOD_STANDARD);
		}

	
		/* Render strips */
		KMShellStrips (&VertexContext[i],
						Mesh[i].nNumStrips, Mesh[i].pStripLength, 
						Mesh[i].pStrips, (KMVERTEX_03 *)KmVert[i]);

	} /* end of meshes loop */
  
	/* Screen text */
	KMABCTablePrint3D (20.0f, 20.0f, 1.0f, 0xFFFFFF00, "Wheel: Bumpmap");
	KMABCTablePrint3D (20.0f, 50.0f, 0.6f, 0xFFFFFFFF, "CURSOR/TRIGGERS to move wheel.");
	KMABCTablePrint3D (20.0f, 70.0f, 0.6f, 0xFFFFFFFF, "CURSOR-A to rotate light.");
	KMABCTablePrint3D (20.0f, 90.0f, 0.6f, 0xFFFFFFFF, "Y/X to zoom.");
  
	nFrame++;
  
	/* RenderScene OK */
	return TRUE;
}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : KMPadControl()                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     : This function is called by KMShell to control the Pad.                           
 *-------------------------------------------------------------------------------------*/
void PadControl (void)
{	
static	int	Delay = 0;

	Delay++;

	/* START */
	if (IsSTARTKey())
	{
		if (Delay>10)
		{
			/* Reset values */
			fAng1 = 0.0f ; fAng2 = -1.2f; 
			fDist = 75.0f;
			fLAng2 = -11.0;
			LightDirection[0] = cos(fLAng2);
			LightDirection[1] = -0.5f;
			LightDirection[2] = sin (fLAng2);
		}
		Delay = 0;
	}

	/* UP */
	if (IsUPKey())
	{
		if (!IsAKey()) 
		{
			/* Wheel forward displacement  */
			fAng1 += fSpeed * 0.1f;   
			fWheelCount+=0.2f;

			/* Ground displacement */
			fDistX += sin(fAng2) * fSpeed * fSincro;
			fDistY += cos(fAng2) * fSpeed * fSincro;

			if (fDistX> 10.0f) fDistX -= 10.0f;
			if (fDistY> 10.0f) fDistY -= 10.0f;

			CalculateGround ();
		}
	}

	/* DOWN */
	if (IsDOWNKey())
	{
		if (!IsAKey()) 
		{
			/* Wheel backward displacement  */
			fAng1 -= fSpeed * 0.1f;  
			fWheelCount-=0.2f;

			/* Ground displacement */
			fDistX -= sin(fAng2) * fSpeed * fSincro;
			fDistY -= cos(fAng2) * fSpeed * fSincro;

			if (fDistX< -10.0f) fDistX += 10.0f;
			if (fDistY< -10.0f) fDistY += 10.0f;
		
			CalculateGround ();
		}
	}

	/* LEFT */
	if (IsLEFTKey())
	{
		/* Light rotation */
		if (IsAKey()) 
		{
			LightDirection[0] = cos(fLAng2);
			LightDirection[1] = -0.5f;
			LightDirection[2] = sin (fLAng2);
			fLAng2 -= 0.02f;
		}
		/* Wheel rotation */
		else  fAng2  += 0.05f;
	}

	/* RIGHT */
	if (IsRIGHTKey())
	{
		/* Light rotation */
		if (IsAKey()) 
		{
			LightDirection[0] = cos(fLAng2);
			LightDirection[1] = -0.5f;
			LightDirection[2] = sin (fLAng2);
			fLAng2 += 0.02f;
		}
		/* Wheel rotation */
		else   fAng2  -= 0.05f;
	}


	/* A button */
	if (IsAKey())
	{
	}
	
	/* B button */
	if (IsBKey())
	{
	}
		
	/* X button */
	if (IsXKey())
	{
		/* Zoom in */
		if (fDist>-40) fDist -= 10.0f;
	}

	/* Y button */
	if (IsYKey())
	{
		/* Zooom out */
		if (fDist<250) fDist += 10.0f;
	}

	/* LEFT finger button */
	if (IsLFKey())
	{
		/* Wheel forward displacement */
		fAng1 += fSpeed * 0.1f;   
		fWheelCount+=0.2f;

		/* Ground displacement */
		fDistX += sin(fAng2) * fSpeed * fSincro;
		fDistY += cos(fAng2) * fSpeed * fSincro;

		if (fDistX> 10.0f) fDistX -= 10.0f;
		if (fDistY> 10.0f) fDistY -= 10.0f;

		CalculateGround ();
	}

	/* RIGHT finger button */
	if (IsRFKey())
	{
		/* Wheel backward displacement */
		fAng1 -= fSpeed * 0.1f;  
		fWheelCount-=0.2f;

		/* Ground displacement */
		fDistX -= sin(fAng2) * fSpeed * fSincro;
		fDistY -= cos(fAng2) * fSpeed * fSincro;

		if (fDistX< -10.0f) fDistX += 10.0f;
		if (fDistY< -10.0f) fDistY += 10.0f;
		
		CalculateGround ();
	}

}
/**************************************************************************************/
/*                                                                                    */
/*                            Aplication functions                                    */
/*                                                                                    */
/**************************************************************************************/
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitMeshes                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    : Shad, Mesh, TempVert, TempNormals                                  
 *  Description     : Sets initials values for meshes managing                            
 *-------------------------------------------------------------------------------------*/
void InitMeshes (void)
{
register unsigned i,j;
float fTempVert[3], fModule;


	/* Memory allocation for KmVert */
	for (i=0; i<NUM_MESHES; i++) 
	{
		/* Object */
		KmVert[i] = (KMVERTEX_03 *)MemPtr; 
		MemPtr += ((KMDWORD)(Mesh[i].nNumVertex*sizeof(KMVERTEX_03)) + 0x1F) & ~0x1F; /* 32Byte Alignment */

	}


	/* Setting UV values and colors for all the meshes */
    for (i=0; i<NUM_MESHES;i++)
	{
		for (j=0; j<Mesh[i].nNumVertex; j++)
		{
			/* 
			 * UVs have been multiplied by a number to get a more wide
			 * range. It's neccessary for the tire model ONLY.
			 */
			if(i==M_TIREBUMP || i==M_TIRE)
			{
				(KmVert[i]+j)->fU = *(Mesh[i].pUV+j*2+0)*8.0f; 
				(KmVert[i]+j)->fV = *(Mesh[i].pUV+j*2+1); 

			}
			else
			{
				(KmVert[i]+j)->fU = (Mesh[i].pUV) ? *(Mesh[i].pUV+j*2+0) : 0.0f; 
				(KmVert[i]+j)->fV = (Mesh[i].pUV) ? *(Mesh[i].pUV+j*2+1) : 0.0f; 

			}
		
			(KmVert[i]+j)->uBaseRGB.dwPacked	= (i==M_PLATES || i==M_GROUND)?0xFFFFFFFF:0xFFA0A0A0;
			(KmVert[i]+j)->uOffsetRGB.dwPacked	= 0x00000000;
		
		} 

   }

}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitBumpmapMesh                                                  
 *  Inputs          : None                                                                 
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                 
 *  Description     : Sets the 'Tangent Space' coordinate system for every vertex to be 
 *					  bumpmapped.                            
 *-------------------------------------------------------------------------------------*/
void InitBumpmapMesh (int Object)
{	
		/* 
		 * Local coordenates system for bump-mapping.
		 * 3 floats for Tanget axis and 3 floats for Binormal Axis
		 */
		pBump[Object] = (float *)MemPtr; 
		MemPtr += ((KMDWORD)(Mesh[Object].nNumVertex*sizeof(float)*6) + 0x1F) & ~0x1F; /* 32Byte Alignment */

		/* 
		 * That is the function that calculates the local coordenate system
		 * for every single vertex. The information is stored in pBump[]
		 */
		KMBumpCalculateLocalCoords (pBump[Object], BUMP_COORDS_ANYDIRECTION,
									Mesh[Object].nNumVertex, Mesh[Object].pVertex, Mesh[Object].pNormals, Mesh[Object].pUV,
									Mesh[Object].nNumFaces, Mesh[Object].pFaces);	
	

}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitMaterials                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     : Loads textures and sets vertex contexts                           
 *-------------------------------------------------------------------------------------*/
void InitMaterials (void)
{
	/* OPAQUE MATERIALS */

	/* Environment map */
	KMShellLoadTexture (&Tex[0],(unsigned long *)Reflect, 256,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VertexContext[M_RIM], &Tex[0], VC_OPAQUE | VC_UVFLIP);
	KMShellInitVertexContext (&VertexContext[M_SUPPORT], &Tex[0], VC_OPAQUE | VC_UVFLIP);
	/* The logo uses additive blending for 'highlight bumpmapping' */
	KMShellInitVertexContext (&VertexContext[M_PLATES], &Tex[0], VC_ADDITIVE);

	/* Base texture for the wheel (opaque for 'translucent bumpmapping')*/
	KMShellLoadTexture (&Tex[5],(unsigned long *)Tire, 128,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VertexContext[M_TIRE], &Tex[5], VC_OPAQUE);

	/* Base texture for the ground (light-map blending for 'standard bumpmapping') */
	KMShellLoadTexture (&Tex[3],(unsigned long *)Ground, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VertexContext[M_GROUND], &Tex[3],  VC_TRANS);
	VertexContext[M_GROUND].SRCBlendingMode		= KM_DESTCOLOR;
	VertexContext[M_GROUND].DSTBlendingMode		= KM_ZERO;
	kmProcessVertexRenderState(&VertexContext[M_GROUND]);


	/* BUMP MATERIALS */

	/* Tire (translucent for 'translucent bumpmapping')*/
	KMShellLoadTexture (&Tex[1],(unsigned long *)TireBump, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_BUMP );
	KMShellInitVertexContext (&VertexContext[M_TIREBUMP], &Tex[1], VC_TRANS | VC_SPECULAR);

	/* Logo (opaque for 'highlight bumpmapping')*/
	KMShellLoadTexture (&Tex[2],(unsigned long *)Logo, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_BUMP );
	KMShellInitVertexContext (&VertexContext[M_PLATESBUMP], &Tex[2], VC_OPAQUE | VC_SPECULAR | VC_DECALALPHA);

	/* Ground (opaque for 'standard bumpmapping')*/
	KMShellLoadTexture (&Tex[4],(unsigned long *)GroundBump, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_BUMP );
	KMShellInitVertexContext (&VertexContext[M_GROUNDBUMP], &Tex[4], VC_OPAQUE | VC_SPECULAR | VC_DECALALPHA);

}

/*-------------------------------------------------------------------------------------* 
 *  Function Name   : CalculateGround                                                       
 *  Inputs          :                                                              
 *  Outputs         :                                                                
  Returns         :                                                                
 *  Globals Used    :                                                                    
 *  Description     : Moves UVs of the ground to simulate that the ground is moving.                            
 *-------------------------------------------------------------------------------------*/
void CalculateGround (void)
{
	(KmVert[M_GROUNDBUMP]+0)->fU = (KmVert[M_GROUND]+0)->fU = 15.0f + fDistX; 
	(KmVert[M_GROUNDBUMP]+0)->fV = (KmVert[M_GROUND]+0)->fV = 15.0f + fDistY; 

	(KmVert[M_GROUNDBUMP]+1)->fU = (KmVert[M_GROUND]+1)->fU = 15.0f + fDistX; 
	(KmVert[M_GROUNDBUMP]+1)->fV = (KmVert[M_GROUND]+1)->fV =  0.0f + fDistY; 

	(KmVert[M_GROUNDBUMP]+2)->fU = (KmVert[M_GROUND]+2)->fU =  0.0f + fDistX; 
	(KmVert[M_GROUNDBUMP]+2)->fV = (KmVert[M_GROUND]+2)->fV =  0.0f + fDistY; 

	(KmVert[M_GROUNDBUMP]+3)->fU = (KmVert[M_GROUND]+3)->fU =  0.0f + fDistX; 
	(KmVert[M_GROUNDBUMP]+3)->fV = (KmVert[M_GROUND]+3)->fV = 15.0f + fDistY; 
}
/*--------------------------------------------------------------------------------------*/
/*---------------------------------- END OF FILE ---------------------------------------*/
/*--------------------------------------------------------------------------------------*/





