/*****************************************************************************
  Name :     Fortune
  Author:	 Carlos Sarria
  Date :     September 1998
  Platform : Dreamcast Set5 (CLX2.2 + SH4)
 
  Description : 1 Gigapixel fill-rate demo.

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

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

#define TOTAL_CARDS 52
#define NUM_CARDS   39

#define CARD_REAL_AREA   154000 

/* Textures have been loaded with Data.c */
extern const unsigned char Reflect[];
extern const unsigned char Shadow[];
extern const unsigned char Table[];
extern const unsigned char Black[];

/* Textures have been loaded with Card.c */
extern const unsigned char Card1[];
extern const unsigned char Card2[];
extern const unsigned char Card3[];
extern const unsigned char Card4[];
extern const unsigned char Card5[];
extern const unsigned char Card6[];
extern const unsigned char Card7[];
extern const unsigned char Card8[];
extern const unsigned char Card9[];
extern const unsigned char Card10[];
extern const unsigned char Card11[];
extern const unsigned char Card12[];
extern const unsigned char Card13[];
extern const unsigned char Card14[];
extern const unsigned char Card15[];
extern const unsigned char Card16[];
extern const unsigned char Card17[];
extern const unsigned char Card18[];
extern const unsigned char Card19[];
extern const unsigned char Card20[];
extern const unsigned char Card21[];
extern const unsigned char Card22[];
extern const unsigned char Card23[];
extern const unsigned char Card24[];
extern const unsigned char Card25[];
extern const unsigned char Card26[];

/*************************************************/
/*	Defines                                      */
/*************************************************/
#ifndef PI
	#define PI 3.14159f
#endif

typedef struct Card_Tag{
	float fVertex[4*3];
	float fNormal[4*3];
	float fU[4];
	float fV[4];
	float fTU[4];
	float fTV[4];
	float fShU[4];
	float fShV[4];
	int   Texture;
	float Displ;
	KMVERTEX_03 KmVert[4];
} CARD_TYPE;


/*************************************************/
/*	Globals                                      */
/*************************************************/
int bStart = FALSE;

int CurrentCard = 0;
float PosZ = 50.0f;

CARD_TYPE   Card[TOTAL_CARDS];

int nCardsList[] = {33,50, 6, 26, 8,34, 3, 9,12,11,
					10,27,25,7,15,30,1,18,37,2,
					13,40,21,31,32, 16,17, 24,19,42,
					39,14,0,22,43,41,46, 5, 4,38, 
					47,49,45,20,44,48,36,23,28,29,
					35,51,52};


int					nFrame = 0;
int 				bIsPacked = FALSE;
static int			nPackCont = 0;

KMSURFACEDESC		TextureTable, TextureShadow, TextureReflect, TextureBlack;
KMSURFACEDESC		TextureCard[256];	
KMVERTEXCONTEXT		VC_Table, VC_Reflect, VC_Shadow, VC_DarkShadow, VC_TransShadow;			
KMVERTEXCONTEXT		VC_Card[256];		

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

int ScreenWidth=640, ScreenHeight=480;

km_vector CameraTo   = { 0.0f, 0.0f, 5000.0f};
km_vector CameraFrom = { 0.0f, -700.0f, -800.0f};
km_vector LightDir   = { 1.0f, -1.0f, 1.0f};

/**************************************************/
/* Prototypes                                     */
/**************************************************/

void	InitMeshes			(void);
void	InitMaterials		(void);
void	CalculateFlyingCard	(int nCard);
void	DrawCard			(int nCard, float Dist);

/************************************************************************/
/* 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     :   InitApplication() is called by KMShell to enable
 *						user to initialise the application                                           
 *----------------------------------------------------------------------*/
void InitApplication (void)
{


	KMShellSetVideo	(TRUE		/* Trans PreSort   */
					 );


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

   

	KMABCTableInit (TRUE);

	InitMaterials ();

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

	/* FREE TEXTURES */
	kmFreeTexture(&TextureTable);
	kmFreeTexture(&TextureReflect);
	kmFreeTexture(&TextureShadow);
	kmFreeTexture(&TextureBlack);

	for (i=0; i<25; i++)
	{
		kmFreeTexture(&TextureCard[i]);
	}
}
/*----------------------------------------------------------------------*
 *  Function Name   :  RenderScene                                      
 *  Inputs          :  None                                               
 *  Outputs         :  None                                                 
 *  Returns         :  FALSE to finish                                               
 *  Globals Used    :  nFrame, CameraTo, CameraFrom, nPT                                                
 *  Description     :  Draws everything in the scene.                                                 
 *----------------------------------------------------------------------*/
int RenderScene (void)
{
float Area;
int i;
MATRIX Matrix;
float fFPS;
 
	KMShellBackground (&VC_Table);

	for (i=0; i<TOTAL_CARDS;i++)
	{
		if (nCardsList[i]<CurrentCard)  DrawCard(nCardsList[i], 0.0f); 
		if (nCardsList[i]==CurrentCard) DrawCard(nCardsList[i], PosZ);
	}

   
    CalculateFlyingCard(CurrentCard);
	
    fFPS = KMTimerGetFPS();

	if(bIsPacked)
	{
		KMABCTablePrint3D(10.0,58.0,0.5,0xFFFFFFFF,"Press A to throw away the cards.");
	}
	else
	{
		KMABCTablePrint3D(10.0,58.0,0.5,0xFFFFFFFF,"Press A to pack the cards.");

	}

	KMABCTablePrint3D(10.0,38.0,0.5,0xFFFFFFFF,"Num Cards = %d   FPS = %2.2f", CurrentCard, fFPS);


	Area = 3.0f*CARD_REAL_AREA*CurrentCard + (640.0f*480.0f*2.0f);

	KMABCTablePrint3D(10.0,10.0,0.8,0xFFFFFF00,"FillRate = %4.2f Mpps %s", 
					(Area*fFPS)/1000000.0f, (bIsPacked)?"":"(approx.)");

	nFrame++;

   /* RenderScene OK */
	return TRUE;
}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : PadControl()                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     :                           
 *-------------------------------------------------------------------------------------*/
void PadControl (void)
{	
register i;
static unsigned nDelay = 0, nTempCurrent;

    nDelay++;

	/* START */
	if (IsSTARTKey())
	{
		bStart = TRUE;
	}
	
	/* UP */
	if (IsUPKey())
	{
	}

	/* DOWN */
	if (IsDOWNKey())
	{
	}

	/* LEFT */
	if (IsLEFTKey())
	{
	}

	/* RIGHT */
	if (IsRIGHTKey())
	{
	}


	/* A button */
	if (IsAKey())
	{
		if (nDelay>10)
		{
			nTempCurrent = CurrentCard;
			bIsPacked = !bIsPacked;
			if(bIsPacked)	for (i=0; i<=CurrentCard;i++)	CalculateFlyingCard (i);
			CurrentCard = nTempCurrent;
		}
		nDelay = 0;
	}
	
	/* B button */
	if (IsBKey())
	{
	}
		
	/* X button */
	if (IsXKey())
	{
	}

	/* Y button */
	if (IsYKey())
	{
	}

	/* LEFT finger button */
	if (IsLFKey())
	{
	}

	/* RIGHT finger button */
	if (IsRFKey())
	{
	}

}
/**************************************************************************************/
/*                                                                                    */
/*                            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)
{
float fCardVert[]   = {-165.0f,-250.0f, 0.0f, 165.0f,-250.0f, 0.0f, 
                       -165.0f, 250.0f, 0.0f, 165.0f, 250.0f, 0.0f};

float fCardUVOdd[]  = {0.0f,0.0f,   0.5f,0.0f,  0.0f,1.0f,   0.5f,1.0f};

float fCardUVEven[] = {0.5f,0.0f,   1.0f,0.0f,  0.5f,1.0f,   1.0f,1.0f};

register i,j;
int nIndex[TOTAL_CARDS], nCard;

	/* Memory allocation for KmVert, UV values and SmoothShading*/
    for (i=0; i<TOTAL_CARDS; i++) 
	{
       
		for (j=0; j<4; j++)
		{
            nCard = nCardsList[i];

			Card[i].fVertex[j*3+0] = fCardVert[j*3+0];
			Card[i].fVertex[j*3+1] = fCardVert[j*3+1];
			Card[i].fVertex[j*3+2] = fCardVert[j*3+2];
						
			Card[i].fU[j] = (nCard&1) ? fCardUVEven[j*2+0]: fCardUVOdd[j*2+0];
			Card[i].fV[j] = (nCard&1) ? fCardUVEven[j*2+1]: fCardUVOdd[j*2+1];
			
			Card[i].KmVert[j].uBaseRGB.dwPacked = 0x40FFFFFF;
			Card[i].KmVert[j].uOffsetRGB.dwPacked = 0xFF000000;

			Card[i].KmVert[j].fU = Card[i].fU[j];
			Card[i].KmVert[j].fV = Card[i].fV[j];

			Card[i].Texture = nCard/2;
			Card[i].Displ = (float)(KMMiscRand()%500-250)/10.0f;
	
		}
		
	}
	
}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitMaterials                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     : Loads textures and sets vertex contexts                           
 *-------------------------------------------------------------------------------------*/
#define LOAD_CARD(x) KMShellLoadTexture (&TextureCard[x-1],(unsigned long *)Card##x, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);\
					 KMShellInitVertexContext (&VC_Card[x-1], &TextureCard[x-1], VC_OPAQUE)

void InitMaterials (void)
{
	KMShellLoadTexture (&TextureTable,(unsigned long *)Table, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VC_Table, &TextureTable, VC_OPAQUE);

	KMShellLoadTexture (&TextureReflect,(unsigned long *)Reflect, 128,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VC_Reflect, &TextureReflect, VC_TRANS);

	/* The blend mode is special for ligth maps: INVSRCCOLOR - SRCCOLOR */
	KMShellLoadTexture (&TextureShadow,(unsigned long *)Shadow, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VC_Shadow, &TextureShadow, VC_TRANS);
	VC_Shadow.FilterMode			= KM_BILINEAR;
	VC_Shadow.SRCBlendingMode		= KM_INVSRCCOLOR;
	VC_Shadow.DSTBlendingMode		= KM_SRCCOLOR;
	kmProcessVertexRenderState(&VC_Shadow);

	KMShellLoadTexture (&TextureBlack,(unsigned long *)Black, 8,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VC_DarkShadow,  &TextureBlack, VC_OPAQUE);
	KMShellInitVertexContext (&VC_TransShadow, &TextureBlack, VC_TRANS);

	/* Cards Contexts */
	LOAD_CARD(1);	LOAD_CARD(2);	LOAD_CARD(3);	LOAD_CARD(4);	LOAD_CARD(5);
	LOAD_CARD(6);	LOAD_CARD(7);	LOAD_CARD(8);	LOAD_CARD(9);	LOAD_CARD(10);
	LOAD_CARD(11);	LOAD_CARD(12);	LOAD_CARD(13);	LOAD_CARD(14);	LOAD_CARD(15);
	LOAD_CARD(16);	LOAD_CARD(17);	LOAD_CARD(18);	LOAD_CARD(19);	LOAD_CARD(20);
	LOAD_CARD(21);	LOAD_CARD(22);	LOAD_CARD(23);	LOAD_CARD(24);	LOAD_CARD(25);
	LOAD_CARD(26);
	

}

/*----------------------------------------------------------------------------------------------------*/
void CalculateFlyingCard (int nCard)
{
register i;
MATRIX Matrix;
CARD_TYPE BufferCard;
static float	PosX = 0.0f, PosY = -1600.0f, AngZ;
static float	AngIncr = -0.3f, PosXIncr = -5.0f, PosYIncr = 20.0f;


TRANSFORM_AGAIN:
	

    AngZ += AngIncr;
	PosX += PosXIncr;
	PosY += PosYIncr;
	PosZ--;

	if (bIsPacked)
	{
		KMMatrixReset(&Matrix);
		KMMatrixTranslate (&Matrix, ((nCard+nPackCont)&1)*340-170, -600.0, 0.0f);
	}
	else
	{
		KMMatrixReset(&Matrix);
		KMMatrixRotate (&Matrix, 0.0f, 0.0f, AngZ);
		KMMatrixTranslate (&Matrix, PosX, PosY, 0.0f);
	}


	KMTransTransformProjectPerspective(&Matrix, 4, Card[nCard].fVertex, Card[nCard].KmVert,
											640, 480,CameraFrom, CameraTo, 0.0f,
											1.0f, 10000.0f, PI/4);

	for (i=0; i<4; i++)
	{
	Card[nCard].fTU[i] = (Card[nCard].KmVert[i].fX+Card[nCard].Displ)/(640.0f-nCard);
	Card[nCard].fTV[i] = 1.0f-(Card[nCard].KmVert[i].fY+Card[nCard].Displ)/(480.0f-nCard);

	Card[nCard].fShU[i] = (Card[nCard].KmVert[i].fX)/640.0f;
	Card[nCard].fShV[i] = 1.0f-(Card[nCard].KmVert[i].fY)/480.0f;
	}


	if (PosZ==0.0f)
	{
		PosX = 0.0f;
		PosY = -1600.0f;
		PosZ = 50.0f;
		AngIncr = -0.22;
		PosXIncr = (float)(KMMiscRand()%140-70)/10.0f;
		PosYIncr = 20.0f - (float)(KMMiscRand()%100-50)/10.0f;
	
		if (nCard<NUM_CARDS)
		{
			CurrentCard++;
		}
		else 
		{
			nPackCont++;

			memcpy(&BufferCard,		&Card[0],		sizeof(CARD_TYPE));
			memcpy(&Card[0],		&Card[1],		sizeof(CARD_TYPE)*(nCard));
			memcpy(&Card[nCard],	&BufferCard,	sizeof(CARD_TYPE));

			KMMatrixReset		(&Matrix);
			KMMatrixRotate		(&Matrix, 0.0f, 0.0f, AngZ);
			KMMatrixTranslate	(&Matrix, PosX, PosY, 0.0f);

			KMTransTransformProjectPerspective(&Matrix, 4, Card[nCard].fVertex, Card[nCard].KmVert,
											640, 480,CameraFrom, CameraTo, 0.0f,
											1.0f, 10000.0f, PI/4);
		}
	}


   

}
/*----------------------------------------------------------------------------------------------------*/
void DrawCard (int nCard, float Dist)
{
register i;
MATRIX Matrix;
unsigned short pLength[] = {2};
unsigned short pFaces[] = {0,1,2,3};

	for(i=0;i<4;i++) 
	{
		Card[nCard].KmVert[i].u.fZ = 1.0f/(float)(60-nCard);

		Card[nCard].KmVert[i].fU = Card[nCard].fU[i];
		Card[nCard].KmVert[i].fV = Card[nCard].fV[i];

	}
	KMShellStrips (&VC_Card[Card[nCard].Texture], 1, pLength, pFaces, Card[nCard].KmVert);
	

	for(i=0;i<4;i++)
	{
		Card[nCard].KmVert[i].u.fZ += 0.0001;

		Card[nCard].KmVert[i].fU = Card[nCard].fShU[i];
		Card[nCard].KmVert[i].fV = Card[nCard].fShV[i];
	}
 
	KMShellStrips (&VC_Shadow, 1, pLength, pFaces, Card[nCard].KmVert);

	for(i=0;i<4;i++)
	{
            Card[nCard].KmVert[i].u.fZ += 0.0001;

			Card[nCard].KmVert[i].fU = Card[nCard].fTU[i];
			Card[nCard].KmVert[i].fV = Card[nCard].fTV[i];
	}

	KMShellStrips (&VC_Reflect, 1, pLength, pFaces, Card[nCard].KmVert);
	
if (Dist > 0.0f && !bIsPacked)
{
	for(i=0;i<4;i++) 
	{
		Card[nCard].KmVert[i].u.fZ = 1.0f/(float)(60-nCard) - 0.0001;
		Card[nCard].KmVert[i].fX -= Dist*3.0f; 
		Card[nCard].KmVert[i].fY += Dist*3.0f; 
		Card[nCard].KmVert[i].fU  = Card[nCard].fU[i];
		Card[nCard].KmVert[i].fV  = Card[nCard].fV[i];
		
	}

	KMShellStrips (&VC_DarkShadow, 1, pLength, pFaces, Card[nCard].KmVert);

	for(i=0;i<4;i++) 
	{
		Card[nCard].KmVert[i].fX += Dist*3.0f; 
		Card[nCard].KmVert[i].fY -= Dist*3.0f; 
	}
}
 

}
