/*****************************************************************************
  Athor:	 Carlos Sarria	
  Name :     Vase
  Date :     Nov 1998
  Platform : Dreamcast Set5 (CLX2 + SH4) 
 
  Description : Accumulation buffer (multitexture) 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 Flora[];
extern const unsigned char Reflect[];


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


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

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

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

/* Special normals for EnvironmentMap 
 * that work for flat surfaces.  
 * They are calculated in InitMeshes()
 */
float	*pEnvNormals[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, -280.0f};
km_vector CameraTo			= { 0.0f, 0.0f, 1000.0f};
km_vector LightDirection	= { 0.0f, -1.0f, 1.0f};

float   nFrame = 0; 
float	fXAng, fYAng, fZAng;

BOOL    bStart = FALSE, bNoAB = FALSE;


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

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

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

	InitMaterials ();

	InitMeshes();

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

	/* FREE TEXTURES */
	kmFreeTexture(&Tex[0]);
	kmFreeTexture(&Tex[1]);
	kmFreeTexture(&Tex[10]);


}

/*----------------------------------------------------------------------*
 *  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;
static float Angle = 0.0f;

   KMShellBackground (&VertexContext[10]);
   
	if(bStart==FALSE)
	{
		fXAng += PI/200.0f;
		fYAng += PI/300.0f;
	}

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

		KMMatrixReset(&Matrx);
		KMMatrixRotate(&Matrx, fXAng, fYAng, fZAng);
	
		KMTransTransformProjectPerspective (&Matrx, Mesh[i].nNumVertex, Mesh[i].pVertex, 
											KmVert[i], ScreenWidth, ScreenHeight,
											CameraFrom, CameraTo, 0.0f, 1.0f, 1000000.0f, 30.0f*(PI/180.0f));

		if (i!=0)
		{
			KMTransEnvironmentMap (&Matrx, Mesh[i].nNumVertex, (i!=2) ? Mesh[i].pNormals : pEnvNormals[i], 
											KmVert[i], 	CameraFrom, CameraTo, 0.0f, 
											1.0f, 1000000.0f, 30.0f*(PI/180.0f));

		}

		if (i!=2)
		{
			KMLightSmoothShade (&Matrx, Mesh[i].nNumVertex, Mesh[i].pNormals, (KMVERTEX_03 *)KmVert[i],
								LightDirection, 0xFFFFFFFF, FALSE);
		}
					
	}
	
	/* Vase cylinder 4444 texture */
	RenderObject (0, &VertexContext[0]);

	/* Vase metallic base */
	RenderObject (1, &VertexContext[1]);

	/* 
	 * Special blend environtment map over vase cylinder 
	 * that is displayed where a pixel is opaque only.
	 */	
	RenderObject (2, &VertexContext[20]);
	RenderObject (0, &VertexContext[21]);
	RenderObject (0, &VertexContext[22]);
		

	KMABCTablePrint3D (10.0f, 20.0f, 1.0f, 0xFFFFFF00, "Accumulation buffer");

	if (bStart==FALSE)  
	{
		KMABCTablePrint3D (20.0f, 50.0f, 0.6f, 0xFF800000, "Press START");
	}
	if (bStart==TRUE)
	{
	   KMABCTablePrint3D (20.0f, 50.0f, 0.6f, 0xFF800000, "CURSOR to move object.");
	}
  
	nFrame++;     

	/* RenderScene OK */
	return TRUE;

}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : PadControl()                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     :                            
 *-------------------------------------------------------------------------------------*/
void PadControl (void)
{	
static	int	Delay = 0;

	Delay++;

	/* START */
	if (IsSTARTKey())
	{
		if (Delay>10) bStart = !bStart;
		Delay = 0;
	}
	
	/* No controler until START is pressed */
	if (bStart == FALSE) return;
	
	/* UP */
	if (IsUPKey())
	{
		fXAng += 0.05f;
	}

	/* DOWN */
	if (IsDOWNKey())
	{
		fXAng  -= 0.05f;
	}

	/* LEFT */
	if (IsLEFTKey())
	{
		fYAng  += 0.05f;
	}

	/* RIGHT */
	if (IsRIGHTKey())
	{
		fYAng  -= 0.05f;
	}


	/* A button */
	if (IsAKey())
	{
	}
	
	/* B button */
	if (IsBKey())
	{
	}
		
	/* 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;
float fTempVert[3], fModule;

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

	/* 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	= 0x80FFFFFF;
			(KmVert[i]+j)->uOffsetRGB.dwPacked	= 0xFF000000;
		
		}     
	}

	/* Creating specials normals for flat surfaces. 
	 * These are the media between the normal and the normalized vector
	 * from the origin to the vertex.
	 */
    for (i=0; i<NUM_MESHES;i++)
	{
		for (j=0; j<Mesh[i].nNumVertex; j++)
		{
          fTempVert[0] = *(Mesh[i].pVertex+j*3+0) - Mesh[i].fCenter[0];
		  fTempVert[1] = *(Mesh[i].pVertex+j*3+1) - Mesh[i].fCenter[1];
		  fTempVert[2] = *(Mesh[i].pVertex+j*3+2) - Mesh[i].fCenter[2];

		  if (fTempVert[0] || fTempVert[1] || fTempVert[2])
		  {
			fModule = 1.0f / sqrt (fTempVert[0]*fTempVert[0]+ fTempVert[1]*fTempVert[1] +fTempVert[2]*fTempVert[2]);
			fTempVert[0] *= fModule;
			fTempVert[1] *= fModule;
			fTempVert[2] *= fModule;
		  }

		  *(pEnvNormals[i]+j*3+0) = (fTempVert[0]+*(Mesh[i].pNormals+j*3+0)) * 0.5f;
		  *(pEnvNormals[i]+j*3+1) = (fTempVert[1]+*(Mesh[i].pNormals+j*3+1)) * 0.5f;
		  *(pEnvNormals[i]+j*3+2) = (fTempVert[2]+*(Mesh[i].pNormals+j*3+2)) * 0.5f;
		
		}     
	}
 
  
}
/*-------------------------------------------------------------------------------------* 
 *  Function Name   : InitMaterials                                                         
 *  Inputs          : None                                                               
 *  Outputs         : None                                                               
 *  Returns         : None                                                               
 *  Globals Used    :                                   
 *  Description     : Loads textures and sets vertex contexts                           
 *-------------------------------------------------------------------------------------*/
void InitMaterials (void)
{
 
	/* STANDARD MIPMAP */ 
	KMShellLoadTexture (&Tex[10],(unsigned long *)Back, 256,   KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VertexContext[10], &Tex[10], VC_OPAQUE);

	KMShellLoadTexture (&Tex[0],(unsigned long *)Flora, 256,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_ARGB4444);
	KMShellInitVertexContext (&VertexContext[0], &Tex[0], VC_TRANS);

	KMShellLoadTexture (&Tex[1],(unsigned long *)Reflect, 128,   KM_TEXTURE_TWIDDLED_MM | KM_TEXTURE_RGB565);
	KMShellInitVertexContext (&VertexContext[1], &Tex[1], VC_OPAQUE);
	KMShellInitVertexContext (&VertexContext[2], &Tex[1], VC_TRANS | VC_CULLING);


	/* 
	 * ACCUMULATION BUFFER
	 */
	/* This is the environment-map texture */
	KMShellInitVertexContext (&VertexContext[20], &Tex[1], VC_TRANS | VC_CULLING);
	VertexContext[20].DepthMode			= KM_GREATEREQUAL;
	VertexContext[20].SRCBlendingMode	= KM_ONE;
	VertexContext[20].DSTBlendingMode	= KM_ZERO;
	VertexContext[20].bSRCSel			= 0;			
	VertexContext[20].bDSTSel			= 1;			
	kmProcessVertexRenderState(&VertexContext[20]); 

	/*  This is the flowers texture.
	 *  In this case we use the alpha chanel just to cut out 
	 *  the environment-map.
	 */
	KMShellInitVertexContext (&VertexContext[21], &Tex[0], VC_TRANS | VC_CULLING);
	VertexContext[21].DepthMode			= KM_GREATEREQUAL;
	VertexContext[21].SRCBlendingMode	= KM_INVSRCALPHA;
	VertexContext[21].DSTBlendingMode	= KM_SRCALPHA;
	VertexContext[21].bSRCSel			= 0;			
	VertexContext[21].bDSTSel			= 1;			
	kmProcessVertexRenderState(&VertexContext[21]);

	/* The final map will be a translucent one */
	KMShellInitVertexContext (&VertexContext[22], NULL, VC_TRANS | VC_CULLING);
	VertexContext[22].DepthMode			= KM_GREATEREQUAL;
	VertexContext[22].SRCBlendingMode	= KM_SRCALPHA;
	VertexContext[22].DSTBlendingMode	= KM_INVSRCALPHA;
	VertexContext[22].bSRCSel			= 1;			
	VertexContext[22].bDSTSel			= 0;			
	kmProcessVertexRenderState(&VertexContext[22]);

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

}


