/*****************************************************************************
  Name : KMKamui.c
  Date : August 1998
  Platform : ANSI compatible

  * Description :
  This file is part of the KAMUI TOOLS library (KMTools.lib) used
  with the SDK demos for PowerVR series 2.

  Set of specific functions for Kamui.

  Copyright : 1998 by VideoLogic Limited. All rights reserved.
******************************************************************************/
#include "KMTools.h"
#include "KMShell.h"

#define MAXVERTEXNUM (20000)
#define VERTEXBUFFERSIZE (0x27F000)

/* Globals */
KMVERTEXBUFFDESC VertexB;

/* the random number generator's state variable*/
static KMDWORD RandomNumber=0;

/* Aligned variable for Maple controler */
KMDWORD Buffer[10];
#define	 PData ((KMDWORD *)(((unsigned long)Buffer+7)&0xFFFFFFF8))

/*----------------------------------------------------------------------*/
/*  Function Name   : InitKamui                                         */
/*  Inputs          : VGAOn fOpaq fOpaqMV fTrans fTransMV fPnchTh       */
/*  Outputs         : None                                              */
/*  Returns         : ErrorCode                                         */
/*  Globals Used    : VertexB                                  */
/*  Description     : Init Kamui Video.                                 */
/*----------------------------------------------------------------------*/
KMDWORD KMSetVideo (int nVideoMode, BOOL bIsSuperSample, BOOL bIsPreSort)
{
KMDWORD ErrorCode;

	switch (nVideoMode)
	{
	case VIDEO_VGA:
		/* VGA 16bits */
		ErrorCode = kmInitDevice(KM_VGA);
		if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;

		ErrorCode = kmSetDisplayMode( KM_DSPMODE_VGA, KM_DSPBPP_RGB565, TRUE, bIsSuperSample);
		if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;

		break;

	case VIDEO_NTSC:
		/* NTCS 320x240 with Supersample */
		ErrorCode = kmInitDevice(KM_NTSC);
		if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;

		ErrorCode = kmSetDisplayMode( KM_DSPMODE_NTSCNI320x240,	KM_DSPBPP_RGB565, TRUE, bIsSuperSample);
		if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;
		break;

	case VIDEO_PAL:
		/* PAL 320x240 with Supersample */
		ErrorCode = kmInitDevice(KM_PAL);
		if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;

		ErrorCode = kmSetDisplayMode( KM_DSPMODE_PALNI320x240,	KM_DSPBPP_RGB565, TRUE, bIsSuperSample);
		if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;
		break;
	}

	/* PRE-SORTING */
	if (bIsPreSort) kmSetAutoSortMode(FALSE);
}
/*----------------------------------------------------------------------*/
/*  Function Name   : KMShellSetBuffers                                */
/*  Inputs          : VGAOn fOpaq fOpaqMV fTrans fTransMV fPnchTh       */
/*  Outputs         : None                                              */
/*  Returns         : ErrorCode                                         */
/*  Globals Used    : VertexB                                  */
/*  Description     : Init Kamui buffers.                               */
/*                    The size of each vertex buffer is given in %      */
/*----------------------------------------------------------------------*/
KMDWORD KMSetBuffers (float fOpaq, float fOpaqMV, float fTrans, float fTransMV,  float fPnchTh)
{
PKMSURFACEDESC			pFB[2];
KMSYSTEMCONFIGSTRUCT	SystemConfig;
KMSURFACEDESC			PrimarySurfaceDesc;
KMSURFACEDESC			BackSurfaceDesc;
static unsigned char	*pCurrentPos;
static int				BuffPtr = 0x0CD80000;
KMDWORD					ErrorCode;

	pFB[0] = &PrimarySurfaceDesc;
	pFB[1] = &BackSurfaceDesc;
	SystemConfig.dwSize	=	sizeof(KMSYSTEMCONFIGSTRUCT);
	SystemConfig.flags	=	KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER;

	/* for Frame buffer */
	SystemConfig.ppSurfaceDescArray	=	pFB;
	SystemConfig.nNumOfFrameBuffer	=	2;
	SystemConfig.nWidth				=	0; /* Not needed */
	SystemConfig.nHeight			=	0; /* Not needed */
	SystemConfig.nBpp				=	0; /* Not needed */

	/* for Texture Memory */
	SystemConfig.nTextureMemorySize	=	(0x400000/sizeof(KMDWORD));	/* 4M byte Texture */

	/* for Vertex buffer */
	SystemConfig.pBufferDesc		=	&VertexB;			/* VertexB */

	pCurrentPos = (unsigned char *)(((unsigned long)BuffPtr + 0x1F) & ~0x1F);
	SystemConfig.pVertexBuffer		=	(PKMDWORD)(((KMDWORD)pCurrentPos  & 0x0FFFFFFF) | 0xA0000000 );

	SystemConfig.nVertexBufferSize	=	(VERTEXBUFFERSIZE/sizeof(KMDWORD));	/* VertexBuffer:1.5MB */

	/* These amounts in % */
	SystemConfig.fBufferSize[0]		=	fOpaq;
	SystemConfig.fBufferSize[1]		=	fOpaqMV;
	SystemConfig.fBufferSize[2]		=   fTrans;
	SystemConfig.fBufferSize[3]		=	fTransMV;
	SystemConfig.fBufferSize[4]		=	fPnchTh;

	/* for Latency Model */
	SystemConfig.nVbufModel			=	 KM_VERTEXBUFMODEL_NORMAL;

	ErrorCode = kmSetSystemConfiguration( &SystemConfig );
	if (ErrorCode != KMSTATUS_SUCCESS) return ErrorCode;

	/* Everything's OK */
	return KMSTATUS_SUCCESS;
}
/*----------------------------------------------------------------------*/
/*  Function Name   : KMShellQuitKamui                                  */
/*  Inputs          : None                                              */
/*  Outputs         : None                                              */
/*  Returns         : None                                              */
/*  Globals Used    : VertexB                                  */
/*  Description     : Free all buffers.                                 */
/*----------------------------------------------------------------------*/
void	KMQuitKamui(void)
{
	/* Free vertex buffer */
	kmDiscardVertexBuffer(&VertexB);

	/* End display */
	kmStopDisplayFrameBuffer();
}
/*----------------------------------------------------------------------*/
/*  Function Name   :  Background                                       */
/*  Inputs          :  *VertexContext                                   */
/*  Outputs         :  None                                             */
/*  Returns         :  None                                             */
/*  Globals Used    :  VertexB                                 */
/*  Description     :  Draws a background polygon.                      */
/*----------------------------------------------------------------------*/
void KMBackground (KMVERTEXCONTEXT *VertexContext)
{
KMVERTEX_03     V[4];
int	i, k;

	V[0].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
	V[1].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
	V[2].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
	V[3].ParamControlWord	= KM_VERTEXPARAM_ENDOFSTRIP;

	/* Colors */
	for (k=0;k<4;k++)
	{
	V[k].uBaseRGB.dwPacked	 = 0xFFFFFFFF;
	V[k].uOffsetRGB.dwPacked = 0x00000000;
	}

    /* Position */
    V[0].fX	= 0.0f;
	V[0].fY	= 480.0f;

	V[1].fX	= 0.0f;
	V[1].fY	= 0.0f;

	V[2].fX	= 640.0f;
	V[2].fY	= 480.0f;

	V[3].fX	= 640.0f;
	V[3].fY	= 0.0f;

	V[0].u.fZ =  0.00001;
	V[1].u.fZ =  0.00001;
	V[2].u.fZ =  0.00001;
	V[3].u.fZ =  0.00001;

	/* UVs */
	V[0].fU	=  0.0f;
	V[0].fV	=  0.0f;

	V[1].fU	=  0.0f;
	V[1].fV	=  1.0f;

	V[2].fU	=  1.0f;
	V[2].fV	=  0.0f;

    V[3].fU	=  1.0f;
	V[3].fV	=  1.0f;

	kmSetVertexRenderState(VertexContext);

	/* Strip start */
	kmxxGetCurrentPtr(&VertexB);
	kmxxStartVertexStrip(&VertexB);

	/* Setting vertices */
	for(i = 0; i<4; i++)
	{
		kmxxSetVertex_3(V[i].ParamControlWord,
							V[i].fX, V[i].fY, V[i].u.fZ,
							V[i].fU, V[i].fV,
							V[i].uBaseRGB.dwPacked, V[i].uOffsetRGB.dwPacked);
	}

    /* Strip end */
	kmxxReleaseCurrentPtr(&VertexB);
}
/*----------------------------------------------------------------------*/
/*  Function Name   :  InitVertexContext                                */
/*  Inputs          :  pVertexContext, *pTexSurfDesc                    */
/*  Outputs         :  None                                             */
/*  Returns         :  None                                             */
/*  Globals Used    :  RenderInfo                                       */
/*  Description     :  Sets standard render parameters.                 */
/*----------------------------------------------------------------------*/
void KMInitVertexContext(PKMVERTEXCONTEXT pVertexContext, KMSURFACEDESC  *pTexSurfDesc, int nMode)
{
int nPoly;

    nPoly = nMode & 3;

    memset(pVertexContext, 0, sizeof(KMVERTEXCONTEXT));

	/*CONTEXT*/
	pVertexContext->RenderState	= KM_DEPTHMODE  		| KM_CULLINGMODE
								| KM_SCREENCOORDINATION	| KM_SHADINGMODE
								| KM_MODIFIER 			| KM_ZWRITEDISABLE
								| KM_SRCBLENDINGMODE	| KM_DSTBLENDINGMODE
								| KM_SRCSELECT			| KM_DSTSELECT
								| KM_FOGMODE			| KM_USESPECULAR
		 						| KM_USEALPHA			| KM_IGNORETEXTUREALPHA
								| KM_FLIPUV 			| KM_CLAMPUV
								| KM_FILTERMODE			| KM_SUPERSAMPLE
								| KM_MIPMAPDADJUST 		| KM_TEXTURESHADINGMODE
								| KM_PARAMTYPE 			| KM_LISTTYPE
								| KM_COLORTYPE			| KM_USESPECULAR
								| KM_UVFORMAT;
    /* Texture */
	if (pTexSurfDesc) pVertexContext->pTextureSurfaceDesc	= pTexSurfDesc;

	pVertexContext->ParamType			= KM_POLYGON;
	pVertexContext->ColorType			= KM_PACKEDCOLOR;
	pVertexContext->UVFormat			= KM_32BITUV;
	pVertexContext->DepthMode			= KM_GREATER;
	pVertexContext->ScreenCoordination	= KM_SCREEN;
	pVertexContext->SelectModifier		= KM_NOMODIFIER;
	pVertexContext->bZWriteDisable		= FALSE;			/* ARC1*/
	pVertexContext->SRCBlendingMode		= KM_ONE;
	pVertexContext->DSTBlendingMode		= KM_ZERO;
	pVertexContext->bSRCSel				= FALSE;			/* ARC1*/
	pVertexContext->bDSTSel				= FALSE;			/* ARC1*/
	pVertexContext->FlipUV				= KM_FLIP_UV;
	pVertexContext->ClampUV				= KM_NOCLAMP;
	pVertexContext->FilterMode			= KM_BILINEAR;
	pVertexContext->bSuperSample		= FALSE;
	pVertexContext->MipMapAdjust		= KM_MIPMAP_D_ADJUST_1_00;
	pVertexContext->TextureShadingMode	= KM_MODULATE_ALPHA;

	/* No effect on ARC1 */
	pVertexContext->bColorClamp		= FALSE;
	pVertexContext->PaletteBank		= 0;

	/* for GlobalColorData */
	pVertexContext->fFaceColorAlpha		= 1.0f;		/* Face Color Alpha */
	pVertexContext->fFaceColorRed		= 1.0f;		/* Face Color Red */
	pVertexContext->fFaceColorGreen		= 1.0f;		/* Face Color Green */
	pVertexContext->fFaceColorBlue		= 1.0f;		/* Face Color Blue */
	pVertexContext->fOffsetColorAlpha	= 0.0f;		/* Specular Color Alpha */
	pVertexContext->fOffsetColorRed		= 0.0f;		/* Specular Color Red */
	pVertexContext->fOffsetColorGreen	= 0.0f;		/* Specular Color Green */
	pVertexContext->fOffsetColorBlue	= 0.0f;		/* Specular Color Blue */

	/* FLIP UV */
	if (nMode & VC_UVFLIP)
		pVertexContext->FlipUV			= KM_FLIP_UV;
	else
		pVertexContext->FlipUV			= KM_NOFLIP;

	/* POLYGON */
	pVertexContext->ListType			= KM_OPAQUE_POLYGON; /* opaque by default */
	if (nPoly==VC_TRANS || nPoly==VC_ADDITIVE)
		pVertexContext->ListType	    = KM_TRANS_POLYGON;
	if (nPoly==VC_PUNCHTHROUGH)
		pVertexContext->ListType	    = KM_PUNCHTHROUGH_POLYGON;

	/* CULLING */
	pVertexContext->CullingMode			= KM_NOCULLING;
	if(nMode & VC_CULLING)
		pVertexContext->CullingMode		= KM_CULLCCW;

	/* TEXTURED */
	pVertexContext->ShadingMode			= KM_TEXTUREGOURAUD;
	if (nMode & VC_NONTEXTURED)
		pVertexContext->ShadingMode		= KM_NOTEXTUREGOURAUD;

	/* BLEND */
	if (nPoly==VC_TRANS | nPoly==VC_PUNCHTHROUGH)
	{
		pVertexContext->SRCBlendingMode		= KM_SRCALPHA;
		pVertexContext->DSTBlendingMode		= KM_INVSRCALPHA;
	}

	if (nPoly==VC_ADDITIVE)
	{
		pVertexContext->SRCBlendingMode		= KM_ONE;
		pVertexContext->DSTBlendingMode		= KM_ONE;
	}

	/* FOG */
	pVertexContext->FogMode				= KM_NOFOG;
	if (nMode & VC_FOGVERTEX)
		pVertexContext->FogMode			= KM_FOGVERTEX;
	if (nMode & VC_FOGTABLE)
		pVertexContext->FogMode			= KM_FOGTABLE;

	/* SPECULAR */
	pVertexContext->bUseSpecular		= FALSE;
	if (nMode & VC_SPECULAR)

		pVertexContext->bUseSpecular	= TRUE;

    /* TRANSLUCENCY */
	pVertexContext->bUseAlpha			= (nPoly==VC_TRANS | nPoly==VC_PUNCHTHROUGH | nPoly==VC_ADDITIVE);


	kmProcessVertexRenderState(pVertexContext);

}
/*---------------------------------------------------------------------------*
 *  Function Name   :  km_rand()
 *  Inputs          :
 *  Outputs         :
 *  Returns         :
 *  Globals Used    :
 *  Description     : A portable L.C.G. random number generator taken from
 *				  Numerical Recipies. It does assume the unsigned 32bit mul
 *				  does behave correctly. ie returns the bottom 32bits of a
 *				  64Bit result. With a seed of 0, the next few numbers should
 *				  be 0x3C6EF35F, 0x47502932, 0xD1CCF6E9, and 0xAAF95334.
 *				  INTERNALLY! To make it easier to use, we mask off the top
 *				  bit so that we can return it as a positive signed integer.
 *---------------------------------------------------------------------------*/
long KMRand(void)
{
	const long TOPBIT = 0x80000000L;

	RandomNumber=1664525UL*RandomNumber + 1013904223UL;

	return(RandomNumber & ~TOPBIT);
}
/*---------------------------------------------------------------------------*
 *  Function Name   :  km_srand()
 *  Inputs          :
 *  Outputs         :
 *  Returns         :
 *  Globals Used    :
 *  Description     : Seeds the random number generator. It can take any value
 *---------------------------------------------------------------------------*/
void KMSeedRand(unsigned long Seed)
{
	RandomNumber=Seed;
}
/*----------------------------------------------------------------------*/
/*  Function Name   : kmtri_strips                                      */
/*  Inputs          : nNumStrips *pLen *pStrips *pVertices              */
/*  Outputs         : None                                              */
/*  Returns         : None                                              */
/*  Globals Used    : VertexB                                  */
/*  Description     : This is a Kamui version of sgltri_index_strips.   */
/*----------------------------------------------------------------------*/
void kmtri_strips (PKMVERTEXCONTEXT pVertexContext, unsigned nNumStrips, unsigned *pLen,
						unsigned *pStrips, KMVERTEX_03 *pVertices)
{
int i, j;
int nVertices;
int nFace, nFacesCont=0;

    kmSetVertexRenderState (pVertexContext);

	/* Textured Vertices KMVERTEX_03 */

	if ((pVertexContext->ShadingMode == KM_TEXTUREGOURAUD) ||
		(pVertexContext->ShadingMode == KM_TEXTUREFLAT))
	{

		for (j=0; j<nNumStrips; j++)
		{
			nVertices = *(pLen+j) + 1;

			/* Strip start */
			kmxxGetCurrentPtr(&VertexB);
			kmxxStartVertexStrip(&VertexB);

			/* Setting vertices */
			for(i = nVertices; i >= 0; i--)
			{
				nFace = *(pStrips+nFacesCont++);
				(pVertices+nFace)->ParamControlWord	= (i!=0)?KM_VERTEXPARAM_NORMAL:KM_VERTEXPARAM_ENDOFSTRIP;

				kmxxSetVertex_3((pVertices+nFace)->ParamControlWord,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->fU, (pVertices+nFace)->fV,
							(pVertices+nFace)->uBaseRGB.dwPacked, (pVertices+nFace)->uOffsetRGB.dwPacked);
			}

			/* Strip end */
			kmxxReleaseCurrentPtr(&VertexB);
		}
	}
	else /* NonTextured  KMVERTEX_00 */
	{
		for (j=0; j<nNumStrips; j++)
		{
			nVertices = *(pLen+j) + 1;

			/* Strip start */
			kmxxGetCurrentPtr(&VertexB);
			kmxxStartVertexStrip(&VertexB);

			/* Setting vertices */
			for(i = nVertices; i >= 0; i--)
			{
				nFace = *(pStrips+nFacesCont++);
				(pVertices+nFace)->ParamControlWord	= (i!=0)?KM_VERTEXPARAM_NORMAL:KM_VERTEXPARAM_ENDOFSTRIP;

				kmxxSetVertex_0((pVertices+nFace)->ParamControlWord,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->uBaseRGB.dwPacked);
			}

			/* Strip end */
			kmxxReleaseCurrentPtr(&VertexB);
		}
	}
}
/*------------------------------------------------------------------------*/
/*  Function Name   : kmtri_triangles                                     */
/*  Inputs          : nNumfaces *pFaces *pVertices                        */
/*  Outputs         : None                                                */
/*  Returns         : None                                                */
/*  Globals Used    : VertexB                                    */
/*  Description     : This is a Kamui version of sgltri_index_triangles.  */
/*                                                                        */
/*  Warning			: To use triangles(one-poly strip) is not recomended  */
/*					  because it generates more pointers in the parameter */
/*					  buffer than longer strips.     					  */
/*------------------------------------------------------------------------*/
void kmtri_triangles (PKMVERTEXCONTEXT pVertexContext, unsigned nNumFaces,
						unsigned *pFaces, KMVERTEX_03	*pVertices)
{
int nFace, nFacesCont=0, i;

	kmSetVertexRenderState (pVertexContext);

	/* Textured Vertices KMVERTEX_03 */

	if ((pVertexContext->ShadingMode == KM_TEXTUREGOURAUD) ||
		(pVertexContext->ShadingMode == KM_TEXTUREFLAT))
	{

		for(i = 0; i < nNumFaces; i++)
		{
			/* Strip start */
			kmxxGetCurrentPtr(&VertexB);
			kmxxStartVertexStrip(&VertexB);

			/* Vertex 1 */
			nFace = *(pFaces+nFacesCont++);
			kmxxSetVertex_3(KM_VERTEXPARAM_NORMAL,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->fU, (pVertices+nFace)->fV,
							(pVertices+nFace)->uBaseRGB.dwPacked, (pVertices+nFace)->uOffsetRGB.dwPacked);

			/* Vertex 2 */
			nFace = *(pFaces+nFacesCont++);
			kmxxSetVertex_3(KM_VERTEXPARAM_NORMAL,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->fU, (pVertices+nFace)->fV,
							(pVertices+nFace)->uBaseRGB.dwPacked, (pVertices+nFace)->uOffsetRGB.dwPacked);

			/* Vertex 3 */
			nFace = *(pFaces+nFacesCont++);
			kmxxSetVertex_3(KM_VERTEXPARAM_ENDOFSTRIP,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->fU, (pVertices+nFace)->fV,
							(pVertices+nFace)->uBaseRGB.dwPacked, (pVertices+nFace)->uOffsetRGB.dwPacked);

			/* Strip end */
			kmxxReleaseCurrentPtr(&VertexB);
		}
	}
	else /* NonTextured */
	{
		for(i = 0; i < nNumFaces; i++)
		{
			/* Strip start */
			kmxxGetCurrentPtr(&VertexB);
			kmxxStartVertexStrip(&VertexB);

			/* Vertex 1 */
			nFace = *(pFaces+nFacesCont++);
			kmxxSetVertex_0(KM_VERTEXPARAM_NORMAL,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->uBaseRGB.dwPacked);

			/* Vertex 2 */
			nFace = *(pFaces+nFacesCont++);
			kmxxSetVertex_0(KM_VERTEXPARAM_NORMAL,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->uBaseRGB.dwPacked);

			/* Vertex 3 */
			nFace = *(pFaces+nFacesCont++);
			kmxxSetVertex_0(KM_VERTEXPARAM_ENDOFSTRIP,
							(pVertices+nFace)->fX, (pVertices+nFace)->fY, (pVertices+nFace)->u.fZ,
							(pVertices+nFace)->uBaseRGB.dwPacked);

			/* Strip end */
			kmxxReleaseCurrentPtr(&VertexB);
		}
	}
}