/*****************************************************************************
  Athor:	 Carlos Sarria	
  Name :     Trilinear
  Date :     July 1998
  Platform : Dreamcast Set5 (CLX2 + SH4) 
 
  Description : Trilinear demo.

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

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

/* Textures have been loaded with Data.c */
extern const unsigned char Back[];
extern const unsigned char Ball[];
extern const unsigned char Tape[];
extern const unsigned char Ball_MM[];
extern const unsigned char Tape_MM[];
extern const unsigned char Info_MM[];
extern const unsigned char Info[];

/* 3D data */
#include "Trilinear.h"


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

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

/* Vertex pointers*/
KMVERTEX_03			*KmVert[256];		

extern KMVERTEXBUFFDESC VertexBufferDesc;	/* is defined in KMShell.c */

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

int ScreenWidth=640, ScreenHeight=480;

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

float   nFrame = 0; 
float	fXPos, fYPos, SphPos[2];

KMDWORD dwMipmapLevel = 4;

KMBOOL    bStart = FALSE;

KMBOOL bIsMipmap = FALSE, bShowBanners = TRUE;

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

void	InitMeshes			(void);
void	InitTextures		(void);
void    InitMaterials       (void);
void	RenderObject		(int Object, KMVERTEXCONTEXT *VertexContext);

void	CalculateMovement	(int nType);

/************************************************************************/
/* 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);	

	KMABCTableInit (TRUE);

	InitTextures  ();
	InitMaterials ();

	InitMeshes();

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

	/* FREE TEXTURES */
	/* kmFreeTexture(&Tex[M_BONES]); */
	

}

/*----------------------------------------------------------------------*
 *  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)
{
int i, j;
MATRIX Matrx;

   KMShellBackground (&VertexContext[10]);
     

   for (j=0; j<2; j++)
   {   
	    /* 
	     * Transformations 
	     */
		CalculateMovement (j);

		for (i=0; i<NUM_MESHES;i++) 
		{

			KMMatrixReset(&Matrx);

			if (i == M_SPHERE)
			{
				KMMatrixRotate(&Matrx, -nFrame/90.0f, -nFrame/30.0f, 0.0f);
	 			KMMatrixTranslate (&Matrx, 0.0f, SphPos[1] + 20.0f,  SphPos[0]);
			}
 
			if (i==M_BANNER2 || i==M_BANNER3 || i==M_BANNER1) 
			{
				KMMatrixTranslate (&Matrx, 0.0f, SphPos[1] + 30.0f,  SphPos[0]);
			}

			/* Trilinear */
			if (j==0)	KMMatrixTranslate (&Matrx, -51.0f, 0.0f, 0.0f);

			/* Mipmap/standard */
			else		KMMatrixTranslate (&Matrx,  51.0f, 0.0f, 0.0f);
	
	
			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));

		}

		/* 
		 * Render 
		 */
		for (i=0; i<2;i++) 
		{
			/* Mipmap/standard */
			if (j==1)
			{
				if(bIsMipmap) RenderObject (i, &VertexContext[i]);
				else		  RenderObject (i, &VertexContext[i+3]);
			}

			if (j==0)
			{
		        /* Trilinear translucent */
				if (i==M_SPHERE)
				{
					
					RenderObject (i, &VertexContext[20]);
					RenderObject (i, &VertexContext[21]); 
					RenderObject (i, &VertexContext[22]);
				
				}
				/* Trilinear opaque */
				if (i==M_TAPE)
				{
					RenderObject (i, &VertexContext[30]);
					RenderObject (i, &VertexContext[31]);
				}

			}
		}
		
		if (bShowBanners)
		{
			if (j==0) 
			{
				/* Trilinear banner */
				RenderObject (M_BANNER1, &VertexContext[40]);
				RenderObject (M_BANNER1, &VertexContext[41]);
				RenderObject (M_BANNER1, &VertexContext[42]);
			}

			if (j==1)
			{
				/* Mipmap/Standard banner */
				if (bIsMipmap) RenderObject (M_BANNER3, &VertexContext[2]);
				else		   RenderObject (M_BANNER2, &VertexContext[5]);
			}
		}

   } /* end of j loop */
   

   KMABCTablePrint3D (10.0f, 20.0f, 1.0f, 0xFFFFFF00, "Trilinear");
   KMABCTablePrint3D (10.0f, 50.0f, 0.6f, 0xFFFFFFFF, " UP/DOWN Mipmap Offset = %1.2f", (float)dwMipmapLevel*0.25f);
   KMABCTablePrint3D (10.0f, 70.0f, 0.6f, 0xFFFFFFFF, " A- Mipmap %s", (bIsMipmap)?"ON":"OFF");
   KMABCTablePrint3D (10.0f, 90.0f, 0.6f, 0xFFFFFFFF, " B- Banners %s", (bShowBanners)?"ON":"OFF");
 

   nFrame++;     

	/* RenderScene OK */
	return TRUE;

}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : PadControl()                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     :                            
 *-------------------------------------------------------------------------------------*/
void PadControl (void)
{	
static unsigned nDelay = 0;
 
    nDelay++;
 
	/* START */
	if (IsSTARTKey())
	{
		bStart = TRUE;
	} 

	/* UP */
	if (IsUPKey())
	{
		if (nDelay >10)
		{
			if (dwMipmapLevel>1) dwMipmapLevel--;
			InitMaterials ();
			nDelay=0;
		}
	}

	/* DOWN */
	if (IsDOWNKey())
	{
		if (nDelay >10)
		{
			if (dwMipmapLevel<15) dwMipmapLevel++;
			InitMaterials ();
			nDelay=0;
		}
	}

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

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


	/* A button */
	if (IsAKey())
	{
		if (nDelay>10)
		{
			bIsMipmap = !bIsMipmap;
			nDelay = 0;
		}
	}
	
	/* B button */
	if (IsBKey())
	{
		if (nDelay>10)
		{
			bShowBanners = !bShowBanners;
			nDelay = 0;
		}
	}
		
	/* X button */
	if (IsXKey())
	{
	}

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

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

	/* RIGHT finger button */
	if (IsRFKey())
	{
	}
}
/*-------------------------------------------------------------------------------------*/
/*  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, k=0;

	/* Memory allocation for KmVert */
	for (i=0; i<NUM_MESHES; i++) 
	{
		KmVert[i] = (KMVERTEX_03 *)MemPtr; MemPtr += Mesh[i].nNumVertex*sizeof(KMVERTEX_03);
	}

	/* Setting UV values and colors for all the meshes */
    for (i=0; i<NUM_MESHES;i++)
	{
		for (j=0; j<Mesh[i].nNumVertex; j++)
		{
            (KmVert[i]+j)->fU	= *(Mesh[i].pUV+j*2+0);
            (KmVert[i]+j)->fV	= *(Mesh[i].pUV+j*2+1);

			(KmVert[i]+j)->uBaseRGB.dwPacked	= 0xFFFFFFFF;
			(KmVert[i]+j)->uOffsetRGB.dwPacked	= 0xFF000000;
		
		}     
	}
 
  
}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitTextures                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     : Loads textures                     
 *-------------------------------------------------------------------------------------*/
void InitTextures (void)
{

	/* STANDARD MIPMAP */ 
	KMShellLoadTexture (&Tex[0],(unsigned long *)Ball_MM, 128,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_ARGB4444);

	KMShellLoadTexture (&Tex[1],(unsigned long *)Tape_MM, 128,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_RGB565);
	
	KMShellLoadTexture (&Tex[2],(unsigned long *)Info_MM, 128,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_ARGB4444);

	/* STANDARD NO MIPMAP */
	KMShellLoadTexture (&Tex[3],(unsigned long *)Ball, 128,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_ARGB4444);

	KMShellLoadTexture (&Tex[4],(unsigned long *)Tape, 128,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);

	KMShellLoadTexture (&Tex[5],(unsigned long *)Info, 128,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_ARGB4444);

	KMShellLoadTexture (&Tex[10],(unsigned long *)Back, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);


}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitMaterials                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     : Sets vertex contexts                           
 *-------------------------------------------------------------------------------------*/
void InitMaterials (void)
{
 
	/* STANDARD MIPMAP */ 
	KMShellInitVertexContext (&VertexContext[0], &Tex[0], VC_TRANS);
	VertexContext[0].MipMapAdjust	= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[0]);

	KMShellInitVertexContext (&VertexContext[1], &Tex[1], VC_OPAQUE);
	VertexContext[1].MipMapAdjust	= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[1]);

	KMShellInitVertexContext (&VertexContext[2], &Tex[2], VC_TRANS);
	VertexContext[2].MipMapAdjust	= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[2]);

	/* STANDARD NO MIPMAP */
	KMShellInitVertexContext (&VertexContext[3], &Tex[3], VC_TRANS);

	KMShellInitVertexContext (&VertexContext[4], &Tex[4], VC_OPAQUE);

	KMShellInitVertexContext (&VertexContext[5], &Tex[5], VC_TRANS);

	KMShellInitVertexContext (&VertexContext[10], &Tex[10], VC_OPAQUE);


	/* 
	 * TRILINEAR
	 * IMPORTANT NOTE: Use mipmapped textures always.
	 */
	/* TRILINEAR TRANSLUCENT*/
	KMShellInitVertexContext (&VertexContext[20], &Tex[0], VC_TRANS);
	VertexContext[20].FilterMode		= KM_TRILINEAR_A;  
	VertexContext[20].DepthMode			= KM_GREATEREQUAL;
	VertexContext[20].SRCBlendingMode	= KM_ONE;
	VertexContext[20].DSTBlendingMode	= KM_ZERO;
	VertexContext[20].bSRCSel			= 0;			
	VertexContext[20].bDSTSel			= 1;		
	VertexContext[20].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[20]); 

	KMShellInitVertexContext (&VertexContext[21], &Tex[0], VC_TRANS);
	VertexContext[21].FilterMode		= KM_TRILINEAR_B;
	VertexContext[21].DepthMode			= KM_GREATEREQUAL;
	VertexContext[21].SRCBlendingMode	= KM_ONE;
	VertexContext[21].DSTBlendingMode	= KM_ONE;
	VertexContext[21].bSRCSel			= 0;			
	VertexContext[21].bDSTSel			= 1;			
	VertexContext[21].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[21]);

	KMShellInitVertexContext (&VertexContext[22], NULL, VC_TRANS);
	VertexContext[22].DepthMode			= KM_GREATEREQUAL;
	VertexContext[22].bSRCSel			= 1;			
	VertexContext[22].bDSTSel			= 0;			
	VertexContext[22].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[22]);

	/* TRILINEAR OPAQUE */
	/* 
	 * IMPORTANT NOTE: For Translucency PreSort TrilinearA must be Translucent ONE-ZERO)
	 * to avoid sorting artifacts.
	 */
	KMShellInitVertexContext (&VertexContext[30], &Tex[1], VC_OPAQUE);
	VertexContext[30].FilterMode		= KM_TRILINEAR_A;
	VertexContext[30].DepthMode			= KM_GREATEREQUAL;
	VertexContext[30].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[30]);

	KMShellInitVertexContext (&VertexContext[31], &Tex[1], VC_TRANS);
	VertexContext[31].FilterMode		= KM_TRILINEAR_B;
	VertexContext[31].DepthMode			= KM_GREATEREQUAL;
	VertexContext[31].SRCBlendingMode	= KM_ONE;
	VertexContext[31].DSTBlendingMode	= KM_ONE;
	VertexContext[31].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[31]);

	/* BANNER (TRANSLUCENT TRILINEAR) */
	KMShellInitVertexContext (&VertexContext[40], &Tex[2], VC_TRANS);
	VertexContext[40].FilterMode		= KM_TRILINEAR_A;  
	VertexContext[40].DepthMode			= KM_GREATEREQUAL;
	VertexContext[40].SRCBlendingMode	= KM_ONE;
	VertexContext[40].DSTBlendingMode	= KM_ZERO;
	VertexContext[40].bSRCSel			= 0;			
	VertexContext[40].bDSTSel			= 1;			
	VertexContext[40].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[40]); 

	KMShellInitVertexContext (&VertexContext[41], &Tex[2], VC_TRANS);
	VertexContext[41].FilterMode		= KM_TRILINEAR_B;
	VertexContext[41].DepthMode			= KM_GREATEREQUAL;
	VertexContext[41].SRCBlendingMode	= KM_ONE;
	VertexContext[41].DSTBlendingMode	= KM_ONE;
	VertexContext[41].bSRCSel			= 0;			
	VertexContext[41].bDSTSel			= 1;			
	VertexContext[41].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[41]);

	KMShellInitVertexContext (&VertexContext[42], NULL, VC_TRANS);
	VertexContext[42].DepthMode			= KM_GREATEREQUAL;
	VertexContext[42].bSRCSel			= 1;			
	VertexContext[42].bDSTSel			= 0;			
	VertexContext[42].MipMapAdjust		= dwMipmapLevel;
	kmProcessVertexRenderState(&VertexContext[42]);


}
/*-------------------------------------------------------------------------------------
 *  Function Name   : RenderObject                                                    
 *  Inputs          : Object                                                           
 *  Outputs         : None                                                             
 *  Returns         : None                                                          
 *  Globals Used    :                                                                 
 *  Description     : Sending a set of triangles to render.                            
 *-------------------------------------------------------------------------------------*/
void RenderObject (int Object, KMVERTEXCONTEXT *pVertexContext)
{
	KMShellStrips (pVertexContext, Mesh[Object].nNumStrips, Mesh[Object].pStripLength,
		          Mesh[Object].pStrips, KmVert[Object]);

}
/*-------------------------------------------------------------------------------------
 *  Function Name   : RenderObject                                                    
 *  Inputs          : Object                                                           
 *  Outputs         : None                                                             
 *  Returns         : None                                                          
 *  Globals Used    :                                                                 
 *  Description     : Sending a set of triangles to render.                            
 *-------------------------------------------------------------------------------------*/
void CalculateMovement (int nType)
{
register i;
float fZPos, fOffset;

	fOffset = nFrame/20.0f;

	if (nType)
	{
		SphPos[0] = 500.0f * sin(nFrame/100.0f) + 500.0f;
		SphPos[1] = sin((SphPos[0]/100.0f + fOffset)) * 20.0f - 30.0f;
	}

	else
	{
		SphPos[0] = 420.0f * sin(nFrame/100.0f) + 500.0f;
		SphPos[1] = cos((SphPos[0]/100.0f + fOffset)) * 20.0f - 30.0f;
	}

	for (i=0; i<Mesh[M_TAPE].nNumVertex; i++) 
	{
		fZPos = *(Mesh[M_TAPE].pVertex+ i*3 + 2); 

		if (nType)
			*(Mesh[M_TAPE].pVertex+ i*3 + 1) = sin((fZPos/100.0f + fOffset)) * 20.0f - 30.0f;
		else
			*(Mesh[M_TAPE].pVertex+ i*3 + 1) = cos((fZPos/100.0f + fOffset)) * 20.0f - 30.0f;
	}

}

