/***********************************************************************************
           Copyright 1998, NEC Electronics Inc. All rights reserved.

Information and source code samples contained herein are provided "as-is",
without representations or warranties, and are subject to change without notice.
NEC Electronics Inc. cannot support modifications or derivative works created from
the sample source code provided. Developers may use, reproduce, and modify portions
or entire sections of the sample source code for the purposes of creating Dreamcast
applications.  Developers may only commercially distribute the executable or binary
portions of the sample source code.  Sample source code contained herein may not be
appropriate for use under all circumstances.  Please consult Developer documentation
provided along with your Dreamcast unit for additional information and suggestions.
************************************************************************************/


/* DESCRIPTION ?????? */

#define _KM_USE_VERTEX_MACRO_L3_
#include <kamui.h>
#include <kamuix.h>
#include <shinobi.h>		/* joystick support */
#include <math.h>
#include <sg_sycbl.h>		/* to identify vga/composite video cable */
#include <sg_sytmr.h>		/* For timing routines */
#include <math.h>
#include <stdlib.h>

/*
#define _STRICT_UNION_
#define _FLAT_MODEL_
#define _CLX_
#define _CLX2_
#include <register.h>
#include <internal.h>
*/
#include "easy3d.h"
#include "DrawText.h"		/* for diagnostic printing to framebuffer */
#include "KMTools.h"			/* For random number generator */

/*
#include "D:\projects\bmptoh\debug\whitrock_bmp.h"
#include "D:\projects\bmptoh\debug\target_bmp.h"
#include "D:\projects\bmptoh\debug\lake_bmp.h"
*/
#include "tilepat_bmp.h"

/*#include "pvrteapot.h"
*/

/*
#include "f111.h"
*/

/*		#define NUM_TEXTURESt 11	*/
/* CAREFULL! */




	#include "model.h"


/*=========================================================================*/

/* FOG GLOBAL VARIABLES */

#define MAX_FOG_PLANE		2048.0
#define MIN_FOG_PLANE		1.0

int FogFactor;		/* hardware-format value */

float fogNear = 10.0;
float fogFar = 300.0;

enum enumFogFormula
{
	LINEAR_0_100,
	LINEAR_50_100,
	LINEAR_75_100,
	TRY1,
	SIN,
	SQUARED,
	CUBED,
	POWER10,
	GL_EXP,
	GL_EXP2,
	SCALED_GL_EXP2,
	STEPx4,
	SOLID,

	NUM_FOGS
} nFogFormula;

char *fogFormulaNames[] =
{
	"LINEAR_0_100",
	"LINEAR_50_100",
	"LINEAR_75_100",
	"TRY1",
	"SIN",
	"SQUARED",
	"CUBED",
	"Power10",
	"GL_EXP, density = .002",
	"GL_EXP2, density = .001",
	"GL_EXP2, scaled to fit",
	"STEPx4",
	"SOLID",
};


enum enumFogMode
{
	eTableFog,
	eVertexFog,
	eDistanceCorrectedVertexFog,
	eNoFog,

	eNumFogModes
} fogMode = eTableFog;

char *fogModeNames[] =
{
	"Table Fog",
	"Vertex Fog",
	"Range-Based Vert Fog",
	"No Fog",
};


/* FOG CONTROL FUNCTIONS */

int computeFogScaleFactorViaMaxZ( float z );
float getWDepthFromIndexAndFogFactor( int index, int factor );
float getDensityAtW( float w );
float getFloatScaleFactorFromHWScaleFactor( int hwFactor );
void SetupFogTable();   /* uses global fogFar, fogFactor, and fogMode */


/*=========================================================================*/

#define FAR_CLIP_PLANE		5000.0		/* this isn't really used */
#define NEAR_CLIP_PLANE		1.0

#define MAX_XFORMS	10000				/* Vertex-pool count */

/* 3D Geometry Pipeline */

void drawPVRObject( Struct_Scene *pScene, D3DMATRIX mPrdoj );

int sceneNum;

/*=========================================================================*/



/* Takes 3 successive renders to get anything visible (single or double-buffering) */

/*
//===========================================================================
//							CONTEXT SUPPORT
*/


#define CTX_MASK_ALL   (KM_PARAMTYPE | KM_LISTTYPE | KM_COLORTYPE | KM_UVFORMAT |       \
                        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_CLAMPUV | KM_FLIPUV |                \
                        KM_FILTERMODE | KM_SUPERSAMPLE | KM_MIPMAPDADJUST |             \
                        KM_TEXTURESHADINGMODE | KM_COLORCLAMP | KM_PALETTEBANK)
#if 0
/* solid opaque */
KMVERTEXCONTEXT DefaultOpaqueColorContext =
{
    CTX_MASK_ALL, KM_POLYGON, /*KM_OPAQUE_POLYGON*/KM_TRANS_POLYGON, KM_PACKEDCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLSMALL, KM_SCREEN, KM_NOTEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_ONE, KM_ZERO, FALSE, FALSE, KM_NOFOG, FALSE, FALSE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, /*KM_POINT_SAMPLE*/KM_BILINEAR, FALSE, 0x4, KM_MODULATE, FALSE,
    0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f
};
#endif

#if 1
/* translucent texture */
KMVERTEXCONTEXT DefaultOpaqueColorContext =
{
    CTX_MASK_ALL, KM_POLYGON, KM_TRANS_POLYGON, KM_PACKEDCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLCCW, KM_SCREEN, KM_NOTEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_SRCALPHA, KM_INVSRCALPHA, FALSE, FALSE, KM_NOFOG, FALSE, FALSE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, KM_BILINEAR, FALSE, 0x4, KM_MODULATE, FALSE,
    0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f
};
#endif

void init_context( KMVERTEXCONTEXT *pContext )
{
	memcpy( pContext, &DefaultOpaqueColorContext, sizeof(KMVERTEXCONTEXT) );
}

/*===========================================================================*/

#define _ASSERT( exprn )		if ( !(exprn) ) doAssert( __FILE__, __LINE__ );

void doAssert( char *filename, int line )
{
	diagPreserveState();		/* preserve text context (x,y,colors,etc) */

	diagSetFGColor( 255, 255, 0 );
	diagSetBGColor( 255, 0, 0 );
	diagUseFrontBuffer();
	diagSetScreenMargin( 15 );
	diagSetXY(0,0);	/* set to top left of the text-window (which is 20,20) */

	diagUseTransparentBackground();

	diagPrintf(
		"*************************************************\n"
		"                ASSERTION FAILURE                \n"
		"                                                 \n"
		"  File:  %-40s\n"
		"                                                 \n"
		"  Line:  %-40d\n"
		"                                                 \n"
		"*************************************************\n",
	 	filename, line );

	/* pause */
	waitVBlanks( 60 * 5 );


	diagRestoreState();			/* restore saved text context */

}




/*===========================================================================*/

#define TRY(x) try_kamui_func( x )

void try_kamui_func( KMSTATUS status )
{
	_ASSERT( status == KMSTATUS_SUCCESS );
}



#define DEGREES_TO_RADIANS(d)  ((d)*3.14159/180.0)



/*===========================================================================*/

#define RGBA(r,g,b,a)	( (int)((a)<<24) + (int)((r)<<16) + (int)((g)<<8) + (int)((b)<<0) )
#define RGB(r,g,b)	(  (0xff<<24) + ((int)(r)<<16) + ((int)(g)<<8) + ((int)(b)<<0) )

/*===========================================================================*/


#define TEXTURE_BUFFER_SIZE			1*1024*1024/2			/* 512 K? */
#define FRAME_BUFFER_ESTIMATE		2*1024*1024			/* 2 MB (640x480x3x2) */



/* Size of vertex buffer. */
#define VBUFFER_SIZE    4*1024*1024/4    /* 8*3*2000 */

/* System Configuration */
PKMSURFACEDESC          psurface[2];            /* array of pointers to surfaces */
KMPACKEDARGB            packedARGB;
KMVERTEXBUFFDESC		vertexBuffer;		/* Vertex-buffer */

KMSYSTEMCONFIGSTRUCT kmsc =
{
	sizeof(KMSYSTEMCONFIGSTRUCT),
	KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER,	/* set any special flags = clear frame buffer */
    psurface,                               /* pointer to array of surface pointers */
    2,                                      /* number of buffers = 2 */
    640,480,                                /* width and height of surface */
    KM_DSPBPP_RGB565,                       /* bits per pixel */
    TEXTURE_BUFFER_SIZE,                    /* allocate amount of VRAM for texture = 2MB */
    &vertexBuffer,                          /* pointer to vertex buffer descriptor */
    0,                                      /* pointer to vertex buffer - ** malloc now on application side */
	2*VBUFFER_SIZE,							/* size in DWORDS of vertex buffer - must be multiple of 32 */
    {50.f,0.f,50.f,00.f,00.f},             /* percentage of vbuf to use for each type of vertex */
    0,0,0,0,0,0,0,                          /* reserved */
    KM_VERTEXBUFMODEL_NORMAL                /* latency model = 3VB */
};


/*===========================================================================*/

KMSTATUS result;
KMSURFACEDESC frontSurfDesc;
KMSURFACEDESC backSurfDesc;
KMVERTEXCONTEXT context;
KMVERTEXCONTEXT additiveContext;




PKMVERTEX_01        BackPlane[3];           /* Background plane. */
KMVERTEX_01         CurrentStrip_01[16];    /* Strip of opaque color triangles. */


typedef struct
{
	/* ALIAS KMVERTEX 5 ON TOP OF OUR VERTEX! */
	KMDWORD	ParamControlWord;
	KMFLOAT	fX;
	KMFLOAT	fY;
	union{
		KMFLOAT	fZ;
		KMFLOAT	fInvW;
	}
#if defined(_STRICT_UNION_)
	u
#endif
	;
	KMFLOAT	fU;
	KMFLOAT	fV;
	/* padding word */
	KMDWORD	Reserved0;
	KMDWORD	Reserved1;
	KMFLOAT	fBaseAlpha;
	KMFLOAT	fBaseRed;
	KMFLOAT	fBaseGreen;
	KMFLOAT	fBaseBlue;
	KMFLOAT	fOffsetAlpha;
	KMFLOAT	fOffsetRed;
	KMFLOAT	fOffsetGreen;
	KMFLOAT	fOffsetBlue;

	/* then we add our own fields */
	float hX, hY, hZ, hW;
	int bFrontClipped;

} SPECIALKMVERTEX5;


SPECIALKMVERTEX5 xformed[MAX_XFORMS];



/*KMSURFACEDESC SurfaceDesc;*/
KMSURFACEDESC Surfaces[20];

KMSURFACEDESC ChartSurfaceDesc;
/*------------------------------------------------------------------------------------------------*/
/* From here down, I've done some cleaning of the code, you'll notice where it stops.
*/


/*
** Enumerations used by the particle system
*/

typedef enum
{
	NORMAL_PARTICLE,				/* Standard particle that falls downwards */
	MULTISAMPLE_PARTICLE,		/* Standard particle with multisample antialiasing */
	SMOKE_PARTICLE					/* Smoke particle that falls up */
} ParticleType;

/*
** typedefs for the particle system
*/

typedef struct
{
	float
		x,						/* Particle Position */
		y,
		z,
		dx,						/* Particle Velocity */
		dy,
		dz,
		particle_size,
		gravity;			/* Acceleration up/down due to gravity */
#if 0
		floor;
	ParticleType
		type;			/* What kind of particle this is */
#endif
} PARTICLE, *PARTICLE_PTR;

/*
** Do all the static initialization and global definitions for the particle system here
*/

#define MAX_MULTISAMPLE_PARTICLES		1600	/* Number of multisample particles allowed */
#define LIGHTMAP_SIZE		16
#define LIGHTMAP_TEXELS (LIGHTMAP_SIZE * LIGHTMAP_SIZE)
#define	TEXEL_SCALE_FACTOR	(1.0f/(400.0f/LIGHTMAP_SIZE))
#define CENTER_OFFSET ((400.0f/LIGHTMAP_SIZE)*0.5)

int
	my_texture = true;

KMVERTEXCONTEXT
	context2,
	blurContext;								/* Context for motion blurred particles */

#pragma aligndata8(mTest)
#pragma aligndata8(mProj)
#pragma aligndata8(mProjectionOnly)
#pragma aligndata8(mTemp)
D3DMATRIX
	mTest,
	mProjectionOnly,						/* The projection matrix only, without rotations & such */
	mProj,
	mTemp;											/* mTemp holds the modelview matrix w/o projection (used for distance corrected fog), I'll pass this in as a parameter later */

KMSURFACEDESC
	odd_texture_handle,
	even_texture_handle,
	white_texture_handle;				/* Handle to a pure white 8x8 texture used as a placeholder */

#pragma aligndata8(particles)
PARTICLE
	particles[MAX_MULTISAMPLE_PARTICLES];		/* Storage for array of particles */

int
	primary_particles,
	secondary_particles,
	physics					=	true,							/* Turn physics on and off */
	multisample_particle_count	= 0,		/* Number of multisample particles currently in arrays */
	size_1_particles = 0;

Uint32
	last_refresh_loop = 0,
	time_refresh_loop,
	time_main_loop,
	time_particle_loop,
	frozen_refresh_loop,
	frozen_main_loop,
	frozen_particle_loop;
#if 1
static unsigned short solid_texture_data[LIGHTMAP_TEXELS];
static unsigned short solid_texture_data2[LIGHTMAP_TEXELS];
#else
#if 1
static unsigned short solid_texture_data[LIGHTMAP_TEXELS] =
	{
		31,-1, 0,-1, 0,-1, 0,0,
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1,
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1,
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1,
		-1, 0,-1, 0,-1, 0,-1, 0
	};
static unsigned short solid_texture_data2[LIGHTMAP_TEXELS] =
	{
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1,
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1,
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1,
		-1, 0,-1, 0,-1, 0,-1, 0,
		 0,-1, 0,-1, 0,-1, 0,-1
	};

#else
  static int solid_texture_data[8*8] =
	{
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1
	};
#endif
#endif

/* Static initialization of a simple cube */

#define CUBE_VERTICES 8
#define CUBE_FACES 12

static float
	cube_vertices[] =  {	-0.5000,	-0.5000,	-0.5000,
												 0.5000,	-0.5000,	-0.5000,
												-0.5000,	 0.5000,	-0.5000,
												 0.5000,	 0.5000,	-0.5000,
												-0.5000,	-0.5000,	 0.5000,
												 0.5000,	-0.5000,	 0.5000,
												-0.5000,	 0.5000,	 0.5000,
												 0.5000,	 0.5000,	 0.5000};
static float
cube_uvs[]	=				{ 0.0, 0.0,
											1.0, 0.0,
											0.0, 0.0,
											1.0, 0.0,
											0.0, 1.0,
											1.0, 1.0,
											0.0, 1.0,
											1.0, 1.0};
static int
	cube_faces[] = {	  0,2,3,
											3,1,0,
											4,5,7,
											7,6,4,
											0,1,5,
											5,4,0,
											1,3,7,
											7,5,1,
											3,2,6,
											6,7,3,
											2,0,4,
											4,6,2};

#define GROUND_VERTICES 4
#define GROUND_FACES 4


/* for this demo, imported objects from max exist in this coordinate system

Y
|  Z
| /---/  landscape is in XZ plane
|/   /
+---- X

*/

typedef struct
{
	float x,y,z;		/* position */
	float rx, ry, rz;	/* facing angle (in degrees) */
} CAMERA;


CAMERA camera;

/*
** Function Prototypes for the particle system
*/

void drawParticleFountain(
	D3DMATRIX  *mProjd );

void randomParticle(
	float X,
	float Y,
	float Z);

void spawnParticle(
	float					X,				/* Initial position */
	float					Y,
	float					Z,
	float					dX,				/* Initial velocity */
	float					dY,
	float					dZ,
	float					particle_size);

void drawGroundPlane( D3DMATRIX mProj );

/* Kevin didn't prototype these */

void projectVertex( SPECIALKMVERTEX5 *pVert );
void clipAndDrawTri( SPECIALKMVERTEX5 *a, SPECIALKMVERTEX5 *b, SPECIALKMVERTEX5 *c );
void SetupContextForFogMode(KMVERTEXCONTEXT *this_context, int fog_mode);
void loadSceneTextures( Struct_Scene *pScene );
void test();
/*void SH4_LoadMatrix( float *temp );*/

/*------------------------------------------------------------------------------------------------*/
/* Sample program mainline
*/
int main( void )
{
	int
		draw_menu = true,		/* If True the menu will be drawn */
		j,
		x,
		y;

#ifdef __GNUC__
	shinobi_workaround();
#endif

	_ASSERT((((int)&mProj) & 0x07)== 0);

	/* Initialize the starting camera position, scene and fog mode */

	camera.x = 0.0;
	camera.y = 80.0;
	camera.z = -400.0;

	camera.rx = 0.0;
	camera.ry = 0.0;
	camera.rz = 0.0;

	sceneNum	= 1;
  fogMode		= eNoFog;

	syTmrGenSetClock(SYD_TMR_CLOCK_P64);

	/*.................... SYSTEM INITIALIZATION ..................*/

	/* Initialize the display device, select video type and set the frame buffer mode. */

	switch( syCblCheckCable() )
	{
	case SYE_CBL_CABLE_VGA:
		sbInitSystem (KM_DSPMODE_VGA, KM_DSPBPP_RGB565, 1);
		break;
	case SYE_CBL_CABLE_NTSC:
        sbInitSystem (KM_DSPMODE_NTSCNI640x480, KM_DSPBPP_RGB565, 1);
		break;
	}

	/* setup border color */

	packedARGB.dwPacked	=	0x00100000;
	kmSetBorderColor(packedARGB);

	/* setup front & back framebuffer structs */

    psurface[0]         =   &frontSurfDesc;
    psurface[1]         =   &backSurfDesc;

	/* generate vertex buffer */

	kmsc.pVertexBuffer	=	(void *)((((int)syMalloc(2*sizeof(KMDWORD)*VBUFFER_SIZE)&0x3FFFFFE0)|0xA0000000)+32);

	/* Submit system configuration */

  kmSetSystemConfiguration(&kmsc);

	/* load the textures used in the starting scene */

	loadSceneTextures( allScenes[sceneNum] );

	/* create a blank white texture from static data */

	TRY( kmCreateTextureSurface( &white_texture_handle, LIGHTMAP_SIZE, LIGHTMAP_SIZE, /*KM_TEXTURE_TWIDDLED |*/ KM_TEXTURE_RGB565 ) );
	TRY( kmLoadTexture( &white_texture_handle, (void*)solid_texture_data, 0, 0 ) );
	TRY( kmCreateTextureSurface( &odd_texture_handle, LIGHTMAP_SIZE, LIGHTMAP_SIZE, /*KM_TEXTURE_TWIDDLED |*/ KM_TEXTURE_RGB565 ) );
	TRY( kmLoadTexture( &odd_texture_handle, (void*)solid_texture_data, 0, 0 ) );
	TRY( kmCreateTextureSurface( &even_texture_handle, LIGHTMAP_SIZE, LIGHTMAP_SIZE, /*KM_TEXTURE_TWIDDLED |*/ KM_TEXTURE_RGB565 ) );
	TRY( kmLoadTexture( &even_texture_handle, (void*)solid_texture_data2, 0, 0 ) );

	/* Initialize the standard vertex context */

	init_context( &context );
	SetupContextForFogMode(&context, fogMode);
	context.bUseSpecular = 1;
	/*	context.pTextureSurfaceDesc = &SurfaceDesc;*/
	context.pTextureSurfaceDesc = &Surfaces[4];
	context.ShadingMode = KM_TEXTUREGOURAUD,
	context.ColorType = KM_FLOATINGCOLOR;
#if 1
	context.FilterMode = KM_BILINEAR;
#else
	context.FilterMode = KM_POINT_SAMPLE;
#endif
	context.TextureShadingMode	=	KM_DECAL;

	SetupFogTable();
	context.pTextureSurfaceDesc = &odd_texture_handle;
	context.ListType						=	KM_OPAQUE_POLYGON;		/* These polygons go int translucent list */

	context2 = context;
	context2.pTextureSurfaceDesc = &even_texture_handle;
	TRY( kmProcessVertexRenderState( &context ) );
	TRY( kmProcessVertexRenderState( &context2 ) );


	/* Create a clone of this context for use with motion blur particles */

	blurContext											= context;
	blurContext.RenderState					=	CTX_MASK_ALL;		/* Set all values */
	blurContext.ParamType						=	KM_SPRITE;			/* Setup for polygons */
	blurContext.ListType						=	KM_TRANS_POLYGON;		/* These polygons go int translucent list */
	blurContext.ColorType						=	KM_PACKEDCOLOR;		/* Use floating point colors */
	blurContext.UVFormat						=	KM_16BITUV;					/* use 32 bit UV values */
	blurContext.DepthMode						=	KM_GREATER;
	blurContext.bZWriteDisable			=	true;
	blurContext.CullingMode					= KM_NOCULLING; /*KM_CULLCCW;*/
	blurContext.ScreenCoordination	=	KM_SCREEN;
	blurContext.ShadingMode					=	KM_NOTEXTUREFLAT;
	blurContext.SelectModifier			=	KM_NOMODIFIER;
	blurContext.bUseSpecular				= false;
	blurContext.SRCBlendingMode			= KM_ONE;		/*KM_SRCALPHA;*/
	blurContext.DSTBlendingMode			= KM_ONE;		/*KM_INVSRCALPHA;*/
	blurContext.bUseAlpha						=	true;
	blurContext.fFaceColorAlpha			=	0.5f;
	blurContext.fFaceColorRed				= 0.3f;
	blurContext.fFaceColorGreen			= 0.1f;
	blurContext.fFaceColorBlue			= 0.033f;
	blurContext.TextureShadingMode	=	KM_MODULATE_ALPHA;
	TRY( kmProcessVertexRenderState( &blurContext ) );

	/* Create a context used for additive blending */

	init_context( &additiveContext );
	additiveContext.SRCBlendingMode = KM_ONE;
	additiveContext.DSTBlendingMode = KM_ONE;
	TRY( kmProcessVertexRenderState( &additiveContext ) );

	/* Force the background to be setup in table fog mode (gives a better background) */

	SetupContextForFogMode(&context, eTableFog);

	/* set up background plane */

	if (1)
	{
		KMVERTEX3 v[3];
		void *pVerts[3];

		int i;

		for (i=0; i < 3; i++)
		{
	  	v[i].ParamControlWord = KM_VERTEXPARAM_NORMAL;
			v[i].fX = 0;
			v[i].fY = 0;
			v[i].u.fInvW = 1.0/5000.0;		/* cant seem to go any further than this? */
			v[i].fU = v[i].fV = 0.5;
			v[i].uBaseRGB.dwPacked = RGB( 0,0,255 );
			v[i].uOffsetRGB.dwPacked = RGBA(0,0,0,0);

			pVerts[i] = &v[i];
		}
		v[0].fX = 320;
		v[0].fY = 240 - 50;
		v[0].uBaseRGB.dwPacked = RGB( 255,255,0 );

		v[1].fX = 320 + 50;
		v[1].fY = 240 + 50;
		v[1].uBaseRGB.dwPacked = RGB( 0,255,255 );

		v[2].fX = 320 - 50;
		v[2].fY = 240 + 50;
		v[2].uBaseRGB.dwPacked = RGB( 255,0, 0 );

		v[2].ParamControlWord = KM_VERTEXPARAM_ENDOFSTRIP;

		v[0].uBaseRGB.dwPacked =
		v[1].uBaseRGB.dwPacked =
		v[2].uBaseRGB.dwPacked = RGB( 0,0, 64 );


   	TRY( kmSetBackGroundRenderState( &context ) );
		TRY( kmSetBackGroundPlane( pVerts, KM_VERTEXTYPE_03, sizeof(KMVERTEX3) ) );
	}

	/* Flip the fog mode on the main context back the way we originally had it */

	SetupContextForFogMode(&context, fogMode);

	/* Setup the fog colors */

	{
		KMPACKEDARGB ar = { RGB(0,0,0) };
		TRY( kmSetFogVertexColor(ar) );	  /* ugh, couldn't just pass an int! */
		TRY( kmSetFogTableColor(ar) );
	}

	/*
	** Setup the stuff needed for the particle system code to work right
	*/

	/* Seed random number generator */

	KMMiscSeedRand(0);

	/*
	** Done with particle stuff
	*/

	/*.......................... ANIMATE ...................*/

	kmSetAutoSortMode(false);

	while (TRUE)
	{
		int x,y,s,j,k;
		D3DMATRIX  mView, mTrans, mRot1, mRot2, mFinal;

		/*
		** Setup timer for main loop
		*/
		time_main_loop		=	syTmrGetCount();
		time_refresh_loop = syTmrGetCount();

		if (1)
		{


			kmSetVertexRenderState( &context );

			kmStartVertexStrip( &vertexBuffer );

			mProj = CreateProjectionMatrix( NEAR_CLIP_PLANE, FAR_CLIP_PLANE, DEGREES_TO_RADIANS(100) );  /*near,far,fov*/
			mProjectionOnly = mProj;

			mRot1 = RotateY( DEGREES_TO_RADIANS(camera.ry) );

			mRot2= RotateX( DEGREES_TO_RADIANS(camera.rx) );
			mTrans = Translate( -camera.x, -camera.y, -camera.z );

			/* mTrans * mRot1 * mRot2 * Projection */

			mTemp = MatrixMult( mTrans, mRot1 );
			mTemp = MatrixMult( mTemp, mRot2 );
			mProj = MatrixMult( mTemp, mProj );



/*			drawPVRObject( allScenes[sceneNum], mProj );*/
			drawParticleFountain( &mProj );
			drawGroundPlane( mProj );
/*			drawTriangleSeries();
*/
		}

#if 0
		/* draw our chart/graph texture */
		/* This should be moved into another module */
		{
			KMVERTEX5 v[4];
			int CVert = 0;

			v[CVert].ParamControlWord 	= KM_VERTEXPARAM_NORMAL;
			v[CVert].fX					= 380;
			v[CVert].fY					= 220+128;
			v[CVert].u.fInvW			= 1.0f;
			v[CVert].fBaseAlpha			= 1.0;
			v[CVert].fBaseRed			= 1.0;
			v[CVert].fBaseGreen			= 1.0;
			v[CVert].fBaseBlue			= 1.0;
			v[CVert].fOffsetAlpha		= 0.0;
			v[CVert].fOffsetRed			= 0.0;
			v[CVert].fOffsetGreen		= 0.0;
			v[CVert].fOffsetBlue		= 0.0;
			v[CVert].fU					= 0.0;
			v[CVert].fV					= 0.0;
			CVert++;

			v[CVert].ParamControlWord 	= KM_VERTEXPARAM_NORMAL;
			v[CVert].fX					= 380+255;
			v[CVert].fY					= 220+128;
			v[CVert].u.fInvW			= 1.0f;
			v[CVert].fBaseAlpha			= 1.0;
			v[CVert].fBaseRed			= 1.0;
			v[CVert].fBaseGreen			= 1.0;
			v[CVert].fBaseBlue			= 1.0;
			v[CVert].fOffsetAlpha		= 0.0;
			v[CVert].fOffsetRed			= 0.0;
			v[CVert].fOffsetGreen		= 0.0;
			v[CVert].fOffsetBlue		= 0.0;
			v[CVert].fU					= 1.0;
			v[CVert].fV					= 0.0;
			CVert++;

			v[CVert].ParamControlWord 	= KM_VERTEXPARAM_NORMAL;
			v[CVert].fX					= 380;
			v[CVert].fY					= 220+255;
			v[CVert].u.fInvW			= 1.0f;
			v[CVert].fBaseAlpha			= 1.0;
			v[CVert].fBaseRed			= 1.0;
			v[CVert].fBaseGreen			= 1.0;
			v[CVert].fBaseBlue			= 1.0;
			v[CVert].fOffsetAlpha		= 0.0;
			v[CVert].fOffsetRed			= 0.0;
			v[CVert].fOffsetGreen		= 0.0;
			v[CVert].fOffsetBlue		= 0.0;
			v[CVert].fU					= 0.0;
			v[CVert].fV					= 0.5;
			CVert++;

			v[CVert].ParamControlWord 	= KM_VERTEXPARAM_ENDOFSTRIP;
			v[CVert].fX					= 380+255;
			v[CVert].fY					= 220+255;
			v[CVert].u.fInvW			= 1.0f;
			v[CVert].fBaseAlpha			= 1.0;
			v[CVert].fBaseRed			= 1.0;
			v[CVert].fBaseGreen			= 1.0;
			v[CVert].fBaseBlue			= 1.0;
			v[CVert].fOffsetAlpha		= 0.0;
			v[CVert].fOffsetRed			= 0.0;
			v[CVert].fOffsetGreen		= 0.0;
			v[CVert].fOffsetBlue		= 0.0;
			v[CVert].fU					= 1.0;
			v[CVert].fV					= 0.5;
			CVert++;

			/* draw with new texture... */
			context.pTextureSurfaceDesc = &ChartSurfaceDesc;
			TRY( kmProcessVertexRenderState( &context ) );
			kmSetVertexRenderState( &context );
			kmStartVertexStrip( &vertexBuffer );

			kmSetVertex( &vertexBuffer, v+0, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );
			kmSetVertex( &vertexBuffer, v+1, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );
			kmSetVertex( &vertexBuffer, v+2, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );
			kmSetVertex( &vertexBuffer, v+3, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );

			context.pTextureSurfaceDesc = &SurfaceDesc;
			TRY( kmProcessVertexRenderState( &context ) );

		}
#endif
		/* Process joystick input for next frame */
		{
			PDS_PERIPHERAL *pJoy;

			float const moveSpeed = 10.0;
			float moveForward = 0.0;
			float moveSideways = 0.0;

			pdExecPeripheralServer();

			pJoy = pdGetPeripheral(PDD_PORT_A0);

			/* rotate camera in */

			camera.ry -= pJoy->x1 / 40.0;
			camera.rx = pJoy->y1 / 3.0;

			/* Special handling to turn test menu on and off */

			if (( pJoy->on & PDD_DGT_TY) && ( pJoy->on & PDD_DGT_TA ))
				draw_menu = true;

			if (( pJoy->on & PDD_DGT_TX) && ( pJoy->on & PDD_DGT_TB ))
				draw_menu = false;


			if ( pJoy->on & PDD_DGT_TY )
				moveForward = moveSpeed;

			if ( pJoy->on & PDD_DGT_TA )
				moveForward = -moveSpeed;

			if ( pJoy->on & PDD_DGT_TX )
				moveSideways = moveSpeed;

			if ( pJoy->on & PDD_DGT_TB )
				moveSideways = -moveSpeed;

			if ( pJoy->on & PDD_DGT_TL )
				camera.y -= 20.0;
			if ( pJoy->on & PDD_DGT_TR )
				camera.y += 20.0;

			if (pJoy->press & PDD_DGT_ST)
				physics = (!physics);

			if (moveForward)
			{
				/* move camera in facing direction by 'moveForward' amount in XZ plane */
				/* camera.ry is facing angle, measured from Z-axis */
				camera.x += -sin(DEGREES_TO_RADIANS(camera.ry)) * moveForward;
				camera.z += cos(DEGREES_TO_RADIANS(camera.ry)) * moveForward;
			}

			if (moveSideways)
			{
				/* and move camera side-to-side */
				camera.x += -sin(DEGREES_TO_RADIANS(camera.ry+90.0)) * moveSideways;
				camera.z += cos(DEGREES_TO_RADIANS(camera.ry+90.0)) * moveSideways;
			}



		} /* end controller */

		kmRender();

		/* Draw the menu if it's turned on */

		time_main_loop	=	syTmrGetCount() - time_main_loop;

		if(draw_menu)
		{
			test();
		}


		kmFlipFrameBuffer();


		/* lets let the flip-frame complete now... */
		if (0)
		{
			KMVERSIONINFO versionInfo;
			int i;
			for (i=0; i < 100000; i++)
				kmGetVersionInfo( &versionInfo );
		}

		/*
		** Stop the loop timer
		*/

		last_refresh_loop			= syTmrGetCount() - time_refresh_loop;
		if(!draw_menu)
		{
			frozen_refresh_loop		= last_refresh_loop;
			frozen_main_loop			=	time_main_loop;
			frozen_particle_loop	=	time_particle_loop;
		}

	}	/*forever*/

	return (0);
}
/*------------------------------------------------------------------------------------------------*/
/* randomParticle---Creates a new particle at a set location with random velocity vectors
*/
void randomParticle(
	float		X,
	float		Y,
	float		Z)
{
	float
		dx,		/* Initial velocities for the particle */
		dy,
		dz;

	/* This is sloppy, but should work for now */

	dx	=	10.0f * (((double)KMMiscRand())/ ((double)0x7fffffffL)) - 5.0f;
	dy = (5.0f * (((double)KMMiscRand())/((double)0x7fffffffL))) + 10.0f;
	dz = 10.0f * (((double)KMMiscRand())/ ((double)0x7fffffffL)) - 5.0f;

	spawnParticle(X,Y,Z,dx,dy,dz,1.0f);
/*	spawnParticle(X,Y,Z,dx,dy,dz,-0.5f,0.0f, 1.0, MULTISAMPLE_PARTICLE); */
/*	spawnParticle(X,Y,Z,dx,dy,dz,-0.5f,0.0f, 1.0, NORMAL_PARTICLE); */

}
/*------------------------------------------------------------------------------------------------*/
/* randomParticle---Creates a new particle at a set location with random velocity vectors
*/
void randomParticle2(
	float		X,
	float		Y,
	float		Z)
{
	float
		dx,		/* Initial velocities for the particle */
		dy,
		dz;

	/* This is sloppy, but should work for now */

	dx	=	2.0f * (((double)KMMiscRand())/ ((double)0x7fffffffL)) - 1.0f;
	dy = (1.0f * (((double)KMMiscRand())/((double)0x7fffffffL))) + 3.0f;
	dz = 2.0f * (((double)KMMiscRand())/ ((double)0x7fffffffL)) - 1.0f;

	spawnParticle(X,Y,Z,dx,dy,dz,0.5f);
/*	spawnParticle(X,Y,Z,dx,dy,dz,-0.5f,0.0f, 1.0, MULTISAMPLE_PARTICLE); */
/*	spawnParticle(X,Y,Z,dx,dy,dz,-0.5f,0.0f, 1.0, NORMAL_PARTICLE); */

}

/*------------------------------------------------------------------------------------------------*/
/* splashParticle---Create several new particles out of an old one, generating a semi-random
** splash-like breakup when they hit the ground
*/
void splashParticle(
	PARTICLE_PTR	this_particle)
{
	float
		dx,
		dy,
		dz,
		new_size;

	/* Shrink the size of the particles we split off */

	new_size = this_particle->particle_size/2.0f;

	dx	=	this_particle->dx * 9.0f;
	dy	=	this_particle->dy * 8.0f;
	dz	=	this_particle->dz	* 9.0f;
	/* Figure out the splatter pattern */

	spawnParticle(
		this_particle->x,
		this_particle->y,
		this_particle->z,
		dx,
		dy,
		dz,
		new_size);

	spawnParticle(
		this_particle->x,
		this_particle->y,
		this_particle->z,
		-dx,
		dy,
		-dz,
		new_size);

	spawnParticle(
		this_particle->x,
		this_particle->y,
		this_particle->z,
		-dx,
		dy,
		dz,
		new_size);

	spawnParticle(
		this_particle->x,
		this_particle->y,
		this_particle->z,
		dx,
		dy,
		-dz,
		new_size);

}
/*------------------------------------------------------------------------------------------------*/
/* spawnParticle---Creates a new particle and sets up all the initial velocity vectors & such.
** Currently, we give velocities in terms of units per frame.  It should really be something more
** like units/second, but I didn't feel like messing with the realtime clock just yet.
**
** This is the bottleneck routine for getting particles created, all creations should come through
** here to insure proper initialization of data and entry into the right lists.  When you create a
** particle it will stay in the system till it meets it's death condition.
*/
void spawnParticle(
	float					X,				/* Initial position */
	float					Y,
	float					Z,
	float					dX,				/* Initial velocity */
	float					dY,
	float					dZ,
	float					particle_size)
{
	PARTICLE_PTR
		this_particle;


	/* Error check, don't create if we're out of space */

	if(multisample_particle_count >= MAX_MULTISAMPLE_PARTICLES)
		return;

	/* Fill in the non-time specific fields first */

	this_particle									= &particles[multisample_particle_count];
	this_particle->x							= X;
	this_particle->y							= Y;
	this_particle->z							= Z;
	this_particle->dx				= dX/10.0f;
	this_particle->dy				= dY/10.0f;
	this_particle->dz				= dZ/10.0f;
	this_particle->particle_size	= particle_size;
	multisample_particle_count++;

}
#define MULTISAMPLE_GRAVITY		-0.005f
#define MULTISAMPLE_FLOOR			0.0f

/*------------------------------------------------------------------------------------------------*/
/* drawParticleFountain---Draws my little fountain object and handles the physics of updating it
** This version of the routine has been simplified to draw untextured particles with vertex color
** only, this should allow a lot more particles to be drawn, much faster too.
** Modified 4/27 to only handle multisample particles.
*/
void drawParticleFountain( D3DMATRIX *mProj )
{
	int
		sample_count,
		particle_number;		/* The particle we're currently working on */
	float
		fourvec[4],
		x,y,z,dx,dy,dz,size,
		cX,
		cY,
		cZ,
		cW,
		cInvW;
	PARTICLE_PTR
		that_particle,			/* pointer to output particle slot for closing up empty spaces */
		this_particle;			/* Pointer to the particle we are currently updating */

	/*
	** Time the duration of the particle loop
	*/
		time_particle_loop	=	syTmrGetCount();
		primary_particles		=	0;

	/* Setup the rendering context we will be using for all the particles */

	kmSetVertexRenderState( &blurContext );
	kmStartVertexStrip( &vertexBuffer );

	/* For however many particle there are */

	this_particle					= &particles[0];
	that_particle					=	this_particle;

/*	mTest = mProj;*/
	SH4_LoadMatrix( mProj );

	kmxxGetCurrentPtr(&vertexBuffer);

	for(	particle_number = 0;
				particle_number < multisample_particle_count;
				particle_number++, that_particle++)
	{

		/* Close up any gaps in the array due to deleted particles */

		if(this_particle != that_particle)
			*this_particle = *that_particle;

		/* Move the particle data into temps for faster handling */

		x			= this_particle->x;
		y			= this_particle->y;
		z			= this_particle->z;
		dx		=	this_particle->dx;
		dy		=	this_particle->dy;
		dz		=	this_particle->dz;
		size	=	this_particle->particle_size;

		/* Loop through this particle to generate however many samples we need */

		for(sample_count = 10;sample_count > 0; sample_count--)
		{
			/* Apply gravity vector */

			dy += MULTISAMPLE_GRAVITY;

			/* Update position of the particle */

			x	+= dx;
			y	+= dy;
			z	+= dz;
			if(y < MULTISAMPLE_FLOOR)
			{
				/* Negate and halve the current dy */
				dx *= 0.9f;
				dy = -(dy*0.1f);
				dz *= 0.9f;
				/* If the particle's energy goes too low, kill it */
				cZ = fabs(dy) + fabs(dx) + fabs(dz);
				if((cZ < 0.05f) && physics)
				{
					break;
				}
				/* Update the position again so it will draw above ground */
				x	+= dx;
				y	+= 0.0f;
				z	+= dz;

				/* break the particle into pieces */
				#if 1
					if(size == 1.0f && this_particle->dy < -.5f && physics)
						randomParticle2(x,y,z);
				#endif

			}
			/*
			** This block handles the transformation and display of the actual particle
			*/

			/* Transform the centerpoint of the particle */

			fourvec[0] = x;
			fourvec[1] = y;
			fourvec[2] = z;
			SH4_XForm_Vec3_to_Vec4( fourvec, fourvec );

			cX = fourvec[0];
			cY = fourvec[1];
			cZ = fourvec[2];
			cW = fourvec[3];
			if (cZ <-cW )
				continue;
			cInvW = 1.0f / cW;
			cX	= (cX * cInvW) * 500.0f + 320.0f;
			cY	= 240.0f - (cY * cInvW) * 500.0f;

			/* Now run a unit vector through the prespective scale only */

			cZ	= (cInvW * 500.0f * (size * mProjectionOnly._11 + cW * mProjectionOnly._31 + mProjectionOnly._41));

			/* setup the color, W and other things that don't change */

			kmxxSetVertex_15(
				KM_VERTEXPARAM_ENDOFSTRIP,
				cX - cZ,
				cY - cZ,
				cInvW,
				cX + cZ,
				cY - cZ,
				cInvW,
				cX + cZ,
				cY + cZ,
				cInvW,
				cX - cZ,
				cY + cZ);
		}

		/* If the sample count was nonzero, it means the particle was deleted */

		if(sample_count ==0)
		{
			/* If physics is on update the particle's position in the array */
			if(physics)
			{
				this_particle->x	=	x;
				this_particle->y	=	y;
				this_particle->z	=	z;
				this_particle->dx	=	dx;
				this_particle->dy	=	dy;
				this_particle->dz	=	dz;
			}
			if(size == 1.0f)
				primary_particles++;
			this_particle++;
		}
	}
	kmxxReleaseCurrentPtr(&vertexBuffer);

	/* Update the total number of live particles running */

	multisample_particle_count		= this_particle - &particles[0];

	/* For testing, if we've got less than the maximum number going spawn more */


	while((multisample_particle_count < MAX_MULTISAMPLE_PARTICLES) && physics)
	{
		if(primary_particles >= 1000)
			break;
		this_particle->x							= CENTER_OFFSET;
		this_particle->y							= 0.0f;
		this_particle->z							= CENTER_OFFSET;
		this_particle->dx							= .60f * (((float)KMMiscRand())/ ((float)0x7fffffffL)) - .3f;
		this_particle->dy							= (.50f * (((float)KMMiscRand())/((float)0x7fffffffL))) + 1.00f;
		this_particle->dz							= .60f * (((float)KMMiscRand())/ ((float)0x7fffffffL)) - .3f;
		this_particle->particle_size	= 1.0f;
		this_particle++;
		multisample_particle_count++;
		primary_particles++;
	}

	/*
	** Time the duration of the particle loop
	*/

	time_particle_loop	=	syTmrGetCount() - time_particle_loop;


}
/*------------------------------------------------------------------------------------------------*/
/* drawGroundPlane---the ground plane with lightmaps
*/

void drawGroundPlane( D3DMATRIX mProj )
{
	int
		ix,iy,
		texel_number,
		vertex_number,
		face_number,
		particle_number;		/* The particle we're currently working on */
	float
		lightR[LIGHTMAP_SIZE][LIGHTMAP_SIZE],
		lightG[LIGHTMAP_SIZE][LIGHTMAP_SIZE],
		lightB[LIGHTMAP_SIZE][LIGHTMAP_SIZE],
		*lightptrR,
		*lightptrG,
		*lightptrB,
		hX,									/* Temporary holders for vertex being transformed */
		hY,
		hZ,
		hW,
		x,
		y,
		z;
	PARTICLE_PTR
		this_particle;			/* Pointer to the particle we are currently updating */

	/* Setup the rendering context we will be using for this frame */

	if(my_texture)
		kmSetVertexRenderState( &context );
	else
		kmSetVertexRenderState( &context2 );

	kmStartVertexStrip( &vertexBuffer );

	/* Clear out the lighting summation array */

	lightptrR = &lightR[0][0];
	lightptrG = &lightG[0][0];
	lightptrB = &lightB[0][0];

	for(texel_number = 0; texel_number < LIGHTMAP_TEXELS; texel_number++)
	{
		*lightptrR++ = 0;
		*lightptrG++ = 0;
		*lightptrB++ = 0;
	}

	/* Roll through all the particles and sum up their contribution to lighting */

	for(
		this_particle = particles, particle_number = 0;
		particle_number < multisample_particle_count;
		this_particle++, particle_number++)
	{
		ix = (this_particle->x + 200.0f)  * TEXEL_SCALE_FACTOR;
		iy = (this_particle->z + 200.0f)  * TEXEL_SCALE_FACTOR;
		if(ix > LIGHTMAP_SIZE-1) ix = LIGHTMAP_SIZE-1;
		if(iy > LIGHTMAP_SIZE-1) iy = LIGHTMAP_SIZE-1;
		if(ix < 0) ix = 0;
		if(iy < 0) iy = 0;
		hY = 1.0f/this_particle->y;
		if (hY < 0.0f) hY = 1.0f;
		if( hY > 1.0f) hY = 1.0f;
		/* Figure lighting contribution */
		if(this_particle->particle_size == 1.0f)
		{
#if 0
			lightR[iy][ix] += 1.0f * hY;
			lightG[iy][ix] += 0.3f * hY;
			lightB[iy][ix] += 0.1f * hY;
#else
			lightR[iy][ix] += 0.3f * hY;
			lightG[iy][ix] += 0.1f * hY;
			lightB[iy][ix] += 0.033f * hY;
#endif
		}
		else
		{
			lightR[iy][ix] += 0.15f * hY;
			lightG[iy][ix] += 0.05f * hY;
			lightB[iy][ix] += 0.0165f * hY;
		}
#if 0
		if(lightR[iy][ix] > 1.0f) lightR[iy][ix] = 1.0f;
			if(lightG[iy][ix] > 1.0f) lightG[iy][ix] = 1.0f;
			if(lightB[iy][ix] > 1.0f) lightB[iy][ix] = 1.0f;
#endif
	}

	/* Drop the lighting information into the texture map */

	lightptrR = &lightR[0][0];
	lightptrG = &lightG[0][0];
	lightptrB = &lightB[0][0];

	if(my_texture)
	{
		for(texel_number = 0; texel_number < LIGHTMAP_TEXELS; texel_number++)
		{
			if(*lightptrR > 1.0f) *lightptrR = 1.0f;
			if(*lightptrG > 1.0f) *lightptrG = 1.0f;
			if(*lightptrB > 1.0f) *lightptrB = 1.0f;
			solid_texture_data[texel_number] =
				((((unsigned short)(*lightptrR++ * 31.0f)) & 0x1f) << 11) |
				((((unsigned short)(*lightptrG++ * 63.0f)) & 0x3f) << 5)  |
				(((unsigned short)(*lightptrB++ * 31.0f)) & 0x1f);
		}
	}
	else
	{
		for(texel_number = 0; texel_number < LIGHTMAP_TEXELS; texel_number++)
		{
			if(*lightptrR > 1.0f) *lightptrR = 1.0f;
			if(*lightptrG > 1.0f) *lightptrG = 1.0f;
			if(*lightptrB > 1.0f) *lightptrB = 1.0f;
			solid_texture_data2[texel_number] =
				((((unsigned short)(*lightptrR++ * 31.0f)) & 0x1f) << 11) |
				((((unsigned short)(*lightptrG++ * 63.0f)) & 0x3f) << 5)  |
				(((unsigned short)(*lightptrB++ * 31.0f)) & 0x1f);
		}
	}


	/* Alternate between two textures */

	if(my_texture)
		TRY( kmLoadTexture( &odd_texture_handle, (void*)solid_texture_data, 0, 0 ) );
	else
		TRY( kmLoadTexture( &even_texture_handle, (void*)solid_texture_data2, 0, 0 ) );

	if(physics) my_texture = !my_texture;


			for (vertex_number=0; vertex_number < CUBE_VERTICES; vertex_number++)
			{
				unsigned int color;

				/* X, Y, Z particle offsets would be applied here */

				x = cube_vertices[vertex_number*3+0] * 400.0f;
				y = cube_vertices[vertex_number*3+1] * 1.0f - 1.0f;
				z = cube_vertices[vertex_number*3+2] * 400.0f;

				/* Multiply the vertex by the projection matrix and save the results  */

				hX = x * mProj._11 + y * mProj._21 + z * mProj._31 + mProj._41;
				hY = x * mProj._12 + y * mProj._22 + z * mProj._32 + mProj._42;
				hZ = x * mProj._13 + y * mProj._23 + z * mProj._33 + mProj._43;
				hW = x * mProj._14 + y * mProj._24 + z * mProj._34 + mProj._44;

				xformed[vertex_number].hX = hX;
				xformed[vertex_number].hY = hY;
				xformed[vertex_number].hZ = hZ;
				xformed[vertex_number].hW = hW;

				/* set clip flags */
				if (hZ <-hW )
					xformed[vertex_number].bFrontClipped = 1;
				else
				{
					xformed[vertex_number].bFrontClipped = 0;
					projectVertex( &xformed[vertex_number] );
				}

				/* Temporarily force the color and UV values*/



				xformed[vertex_number].fU = cube_uvs[vertex_number*2];
				xformed[vertex_number].fV = cube_uvs[vertex_number*2+1];

				xformed[vertex_number].fBaseAlpha = 0.5f;
				xformed[vertex_number].fBaseRed   = 0.5f;
				xformed[vertex_number].fBaseGreen = 0.1f;
				xformed[vertex_number].fBaseBlue  = 0.02f;

				xformed[vertex_number].fOffsetRed = 0.0;
				xformed[vertex_number].fOffsetGreen = 0.0;
				xformed[vertex_number].fOffsetBlue = 0.0;
				xformed[vertex_number].fOffsetAlpha = 0.0;

			}

			/* And draw the triangles */
			for (face_number=0; face_number < CUBE_FACES; face_number++)
			{
				clipAndDrawTri(
					&xformed[cube_faces[face_number*3+0]],
					&xformed[cube_faces[face_number*3+1]],
					&xformed[cube_faces[face_number*3+2]] );
			}

}

/*------------------------------------------------------------------------------------------------*/
/* SetupContextForFogMode---Modifies a context to use the current fog state
** Changed this to accept parameters rather than globals so it could be used with several
** contexts.
*/
void SetupContextForFogMode(KMVERTEXCONTEXT *this_context, int fog_mode)
{
	switch ( fog_mode )
	{
	case eTableFog:
		this_context->FogMode = KM_FOGTABLE;
		break;

	case eVertexFog:
	case eDistanceCorrectedVertexFog:
		this_context->FogMode = KM_FOGVERTEX;
		break;

	case eNoFog:
		this_context->FogMode = KM_NOFOG;
		break;
	}
	TRY( kmProcessVertexRenderState( this_context ) );
}

/*
** Back to kevin's code
*/


/*------------------------------------------------------------------------------------------------*/
/* TODO: Use a viewport structure here... */

void projectVertex( SPECIALKMVERTEX5 *pVert )
{
	pVert->fX = pVert->hX / pVert->hW * 500 + 320;
	pVert->fY = 240 - pVert->hY / pVert->hW * 500;
	pVert->u.fInvW = 1.0 / pVert->hW;

/* We could compute vertex-fog values at this point,
   but as the camera crosses across a [subdivided] polygon,
   the closest vertex will always be at a fixed distance,
   and the fog gradient may appear to compress in the
   remaining (visible) portion of the poly.

  	if ( bVertexFog )
		xformed[i].fOffsetAlpha = hW/150;
*/
}


/*
intersectFrontPlane() -

Computes new point at front plane between pt A and pt B; places into pt C

Inputs:
a,b: Vertices (one must be clipped, one must be unclipped)
c:   Ptr to vertex that will receive intersection vertx

returns: none

*/

void intersectFrontPlane( SPECIALKMVERTEX5 *a, SPECIALKMVERTEX5 *b, SPECIALKMVERTEX5 *c )
{
	float t;

	/* compute front plane intersection point (t) on line segment AB */
	t = (b->hW + b->hZ) / (b->hW + b->hZ - (a->hW + a->hZ) );

	/* and linearly interpolate all components between A & B via t */
	c->hX = (a->hX - b->hX) * t + b->hX;
	c->hY = (a->hY - b->hY) * t + b->hY;
	c->hZ = (a->hZ - b->hZ) * t + b->hZ; /* optimization: unless doing further clipping, can probably discard Z */
	c->hW = (a->hW - b->hW) * t + b->hW; /* optimization: front plane clip should result in w = NEAR_CLIP_PLANE */

	c->fBaseRed   = (a->fBaseRed   - b->fBaseRed)   * t + b->fBaseRed;
	c->fBaseGreen = (a->fBaseGreen - b->fBaseGreen) * t + b->fBaseGreen;
	c->fBaseBlue  = (a->fBaseBlue  - b->fBaseBlue)  * t + b->fBaseBlue;
	c->fBaseAlpha = (a->fBaseAlpha - b->fBaseAlpha) * t + b->fBaseAlpha;

	c->fOffsetRed   = (a->fOffsetRed   - b->fOffsetRed)   * t + b->fOffsetRed;
	c->fOffsetGreen = (a->fOffsetGreen - b->fOffsetGreen) * t + b->fOffsetGreen;
	c->fOffsetBlue  = (a->fOffsetBlue  - b->fOffsetBlue)  * t + b->fOffsetBlue;
	c->fOffsetAlpha = (a->fOffsetAlpha - b->fOffsetAlpha) * t + b->fOffsetAlpha;

	c->fU = (a->fU - b->fU) * t + b->fU;
	c->fV = (a->fV - b->fV) * t + b->fV;

	c->bFrontClipped = 0;
	projectVertex( c );
}



void drawTri( SPECIALKMVERTEX5 *a, SPECIALKMVERTEX5 *b, SPECIALKMVERTEX5 *c )
{

/*  Disable gouraud shading; everything is white
	a->fBaseRed = a->fBaseGreen = a->fBaseBlue  = a->fBaseAlpha = 1.0;
	b->fBaseRed = b->fBaseGreen = b->fBaseBlue  = b->fBaseAlpha = 1.0;
	c->fBaseRed = c->fBaseGreen = c->fBaseBlue  = c->fBaseAlpha = 1.0;
*/

	a->ParamControlWord = KM_VERTEXPARAM_NORMAL;
	b->ParamControlWord = KM_VERTEXPARAM_NORMAL;
	c->ParamControlWord = KM_VERTEXPARAM_ENDOFSTRIP;

	kmSetVertex( &vertexBuffer, (void*)a, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );
	kmSetVertex( &vertexBuffer, (void*)b, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );
	kmSetVertex( &vertexBuffer, (void*)c, KM_VERTEXTYPE_05, sizeof(KMVERTEX5) );
}


/*
clipAndDrawTri() -
Accepts three vertices of a triangle, checks for clipping against the front plane,
subdivides if necessary, and draws triangle(s).

This works by compositing the front-plane clip flags into a 3 bit code, which
represents if each vertex (a,b,c) is clipped (1) or not clipped (0). There can
be 8 possible outcomes. A clipped triangle can have at most 2 intersecting
edges. The intersection points are computed on the stack (new1 and new2),
submitted for drawing, and discarded.

*/

void clipAndDrawTri( SPECIALKMVERTEX5 *a, SPECIALKMVERTEX5 *b, SPECIALKMVERTEX5 *c )
{
	SPECIALKMVERTEX5 new1, new2;
	int clipCode;

	clipCode =  ( a->bFrontClipped ? 4 : 0 ) +
				( b->bFrontClipped ? 2 : 0 ) +
				( c->bFrontClipped ? 1 : 0 );

	switch (clipCode)
	{
	case 0:  /* 000 (no vertices clipped, just draw) */
		drawTri( a, b, c);
		break;
	case 1:  /* 001 (vertex C is clipped; intersect edges AC and BC) */
		intersectFrontPlane(a, c, &new1);
		intersectFrontPlane(b, c, &new2);
		drawTri( a,  b, &new2);
		drawTri( a, &new2, &new1);
		break;
	case 2:  /* 010 */
		intersectFrontPlane(a, b, &new1);
		intersectFrontPlane(c, b, &new2);
		drawTri( a, &new1, &new2);
		drawTri( a, &new2,  c);
		break;
	case 3:  /* 011 */
		intersectFrontPlane(a, b, &new1);
		intersectFrontPlane(a, c, &new2);
		drawTri( a, &new1, &new2);
		break;
	case 4:  /* 100 */
		intersectFrontPlane(b, a, &new1);
		intersectFrontPlane(c, a, &new2);
		drawTri( b,  c, &new2);
		drawTri( b, &new2, &new1);
		break;
	case 5:  /* 101 */
		intersectFrontPlane(b, a, &new1);
		intersectFrontPlane(b, c, &new2);
		drawTri( b, &new2, &new1);
		break;
	case 6:  /* 110 */
		intersectFrontPlane(c, a, &new1);
		intersectFrontPlane(c, b, &new2);
		drawTri( c, &new1, &new2);
		break;
	case 7:  /* 111 (all vertices clipped; discard) */
		break;
	}
}





/*------------------------------------------------------------------------------------------------*/
/* drawPVRObjedt---Draws an object from the PowerVR object exporter
*/

void drawPVRObject( Struct_Scene *pScene, D3DMATRIX mProj )
{
	int i,j, mesh;
	float hX, hY, hZ, hW;
	Struct_Mesh *pMesh;

	for (mesh=0; mesh < pScene->nMeshes; mesh++)
	{
		/***** Eeech, what a mess! *****/
		int textureindex;
		pMesh = &pScene->pMeshes[mesh];


/*		textureindex = Materialt[pScene->pMeshes[mesh].nMaterial].nTextureIndex;*/

		if ( pMesh->nMaterial == -1)
			textureindex = 0;
		else
			textureindex = pScene->pMaterials[pMesh->nMaterial].nTextureIndex;

#if 1
		/* update clamp/wrap flags- currently we do the same to U & V */
		if ( textureindex >= 0 )
		{
			/* KM_FLIPUV seems broken */
			context.RenderState |= KM_CLAMPUV;

			switch ( pScene->pTextures[textureindex].eFormat )
			{
			case 0:		/* clamp */
				context.ClampUV = KM_CLAMP_UV;
				break;
			case 1:		/* wrap */
				context.ClampUV = KM_NOCLAMP;
				break;
			case 2:		/* mirror */
				context.ClampUV = KM_NOCLAMP;
				break;
			}
		}
#endif

/*		if (textureindex == -1) textureindex = pScene->nTextures; */  /* must have something for now */

		context.pTextureSurfaceDesc = &Surfaces[textureindex];


		TRY( kmProcessVertexRenderState( &context ) );
		kmSetVertexRenderState( &context );
		kmStartVertexStrip( &vertexBuffer );

		/* Transform vertices... */
		for (i=0; i < pMesh->nNumVertex; i++)
		{
			float x,y,z;
			unsigned int color;

			x = pMesh->pVertex[i*3+0];
			y = pMesh->pVertex[i*3+1];
			z = pMesh->pVertex[i*3+2];

			hX = x * mProj._11 + y * mProj._21 + z * mProj._31 + mProj._41;
			hY = x * mProj._12 + y * mProj._22 + z * mProj._32 + mProj._42;
			hZ = x * mProj._13 + y * mProj._23 + z * mProj._33 + mProj._43;
			hW = x * mProj._14 + y * mProj._24 + z * mProj._34 + mProj._44;

			xformed[i].hX = hX;
			xformed[i].hY = hY;
			xformed[i].hZ = hZ;
			xformed[i].hW = hW;

			/* set clip flags */
			if (hZ <-hW )
				xformed[i].bFrontClipped = 1;
			else
			{
				xformed[i].bFrontClipped = 0;
				projectVertex( &xformed[i] );
			}


			if (pMesh->pVertexColor)
				color = pMesh->pVertexColor[i];
			else
			{
				/* texture maps usually are set to replace material colors 100% */
/*				if (textureindex != pScene->nTextures) */
				if (textureindex != 0  ||  pMesh->nMaterial==-1)
					color = RGB(255,255,255);
				else
					color = pScene->pMaterials[pMesh->nMaterial].nMatDiffuse;
			}

				/*-KK, temp forceing of color */
/*			color = RGB(255,128,128);
			color = RGB(255,255,255);
*/
			if (pMesh->pUV)
			{
				xformed[i].fU = pMesh->pUV[i*2];
				xformed[i].fV = pMesh->pUV[i*2+1];
			}
			else
			{
				xformed[i].fU = 0.5;
				xformed[i].fV = 0.5;
			}



			xformed[i].fBaseAlpha = (color >> 24 & 0xff) / 255.0;
			xformed[i].fBaseRed   = (color >> 16 & 0xff) / 255.0;
			xformed[i].fBaseGreen = (color >> 8  & 0xff) / 255.0;
			xformed[i].fBaseBlue  = (color >> 0  & 0xff) / 255.0;

			xformed[i].fOffsetRed = 0.0;
			xformed[i].fOffsetGreen = 0.0;
			xformed[i].fOffsetBlue = 0.0;
			xformed[i].fOffsetAlpha = 0.0;

			if( fogMode == eVertexFog )
				xformed[i].fOffsetAlpha = getDensityAtW( hW );
			else if ( fogMode == eDistanceCorrectedVertexFog )
			{
				float wX, wY, wZ, wW;

				wX = x * mTemp._11 + y * mTemp._21 + z * mTemp._31 + mTemp._41;
				wY = x * mTemp._12 + y * mTemp._22 + z * mTemp._32 + mTemp._42;
				wZ = x * mTemp._13 + y * mTemp._23 + z * mTemp._33 + mTemp._43;
				wW = x * mTemp._14 + y * mTemp._24 + z * mTemp._34 + mTemp._44;

				xformed[i].fOffsetAlpha = getDensityAtW( sqrt( wZ*wZ + wX*wX + wY*wY) );
			}


		}

		/* And draw the triangles */
		for (i=0; i < pMesh->nNumFaces; i++)
		{
			clipAndDrawTri(
				&xformed[pMesh->pFaces[i*3+0]],
				&xformed[pMesh->pFaces[i*3+1]],
				&xformed[pMesh->pFaces[i*3+2]] );
		}
	} /* next mesh in model */
}


/*
Draws a series of triangles with progressive depth values
(for table fog experimentation)

It gets drawn under the chart, so you can see the fog colors applied to a thin
bar that lines up with the graph. (however, I need z-buffer control to keep the
bar visible).
*/

void drawTriangleSeries()
{
	KMVERTEX3 verts[4];
	int i;

	int j;
	for (j=1; j <= 256; j++)
	{
		float x,y;
		float width = 4;
		float height = 6;
		float oow;

		x = 380+j;
		y = 474;
		oow = 1.0 / (j*2048.0/256.0);

		i = 0;
		verts[i].ParamControlWord = KM_VERTEXPARAM_NORMAL;
		verts[i].fX = x;
		verts[i].fY = y;
		verts[i].u.fInvW = oow;
		verts[i].fU = verts[i].fV = 0.0;
		verts[i].uBaseRGB.dwPacked = RGB( 255,0,0 );
		verts[i].uOffsetRGB.dwPacked = RGBA(0,0,0,255);

		i = 1;
		verts[i].ParamControlWord = KM_VERTEXPARAM_NORMAL;
		verts[i].fX = x+width;
		verts[i].fY = y+height/2;
		verts[i].u.fInvW = oow;
		verts[i].fU = verts[i].fV = 0.0;
		verts[i].uBaseRGB.dwPacked = RGB( 255,0,0 );
		verts[i].uOffsetRGB.dwPacked = RGBA(0,0,0,255);

		i = 2;
		verts[i].ParamControlWord = KM_VERTEXPARAM_ENDOFSTRIP;
		verts[i].fX = x;
		verts[i].fY = y+height;
		verts[i].u.fInvW = oow;
		verts[i].fU = verts[i].fV = 0.0;
		verts[i].uBaseRGB.dwPacked = RGB( 255,0,0 );
		verts[i].uOffsetRGB.dwPacked = RGBA(0,0,0,255);

		kmSetVertex( &vertexBuffer, &verts[0], KM_VERTEXTYPE_03+0, sizeof(KMVERTEX3) );
		kmSetVertex( &vertexBuffer, &verts[0], KM_VERTEXTYPE_03+1, sizeof(KMVERTEX3) );
		kmSetVertex( &vertexBuffer, &verts[0], KM_VERTEXTYPE_03+2, sizeof(KMVERTEX3) );

	}

}







float getDensityAtW( float w )
{
	float fog;
	float start, end, density;

	switch (nFogFormula)
	{
	case LINEAR_0_100:
		fog = w / fogFar;
		break;

	case LINEAR_50_100:
		start = fogFar * 0.5;
		end = fogFar;
		fog = (w - start) / (fogFar - start);
		break;

	case LINEAR_75_100:
		start = fogFar * 0.75;
		end = fogFar;
		fog = (w - start) / (end - start);
		break;

	case TRY1:
		fog = (w < fogFar/2) ?  w/fogFar*2.0 : (fogFar-w)/fogFar*2.0;
		break;

	case SIN:
		fog = sin( DEGREES_TO_RADIANS(w/fogFar*360*4) ) * 0.5 + 0.5;
		break;

	case SQUARED:
		fog = pow(w/fogFar,2);
		break;

	case CUBED:
		fog = pow(w/fogFar,3);
		break;

	case POWER10:
		fog = pow(w/fogFar,10);
		break;

	case GL_EXP:
		/* near saturation at w = 2048 */
		density = .002;
		fog = 1.0 - exp ( -density*w ) ;
		break;

	case GL_EXP2:
		/* near saturation at w = 2048 */
		density = .001;
		fog = 1.0 - exp ( -pow(density*w, 2.0) ) ;
		break;

	case SCALED_GL_EXP2:
		/* scale depth range from 0->1 */
		density = 2.048;
		fog = 1.0 - exp ( -pow(density*w/fogFar, 2.0) ) ;
		break;


	case STEPx4:
		{
			fog = (int)(w/fogFar*4)/4.0;
		}
		break;

	/* Solid fog, except force 0 at first index, and 0 at last, so we can see the range */
	case SOLID:
		fog = 1.0;
		if ( w >= getWDepthFromIndexAndFogFactor( 0,   FogFactor )  ||   /* furthest */
		     w <= getWDepthFromIndexAndFogFactor( 128, FogFactor )  )    /* closest */
			fog = 0.0;
		break;


	default:
		fog = 0.0;
		break;
	}


	if (fog < 0.0) fog = 0.0;
	if (fog > 1.0) fog = 1.0;
	return (fog);
}




void SetupFogTable()
{
	float fogTable[256];
	int i;

	{
		float WDepth;

		FogFactor = computeFogScaleFactorViaMaxZ( fogFar );

		for (i=0; i <= 128; i++)
		{
			float fog;
			WDepth = getWDepthFromIndexAndFogFactor( i, FogFactor );

			fog = getDensityAtW( WDepth );

			if (fog < 0.0) fog = 0.0;
			if (fog > 1.0) fog = 1.0;
			fogTable[i] = fog;
		}

		TRY( kmSetFogDensity( FogFactor ) );
		TRY( kmSetFogTable( fogTable ) );
	}


	/* Also, lets create a texture/graph of the fog table */
	{
		static int bFirstTime = 1;
		static int srcTexture[256*256];
		static KMWORD dstTexture[256*256];
		int x,y;
		float xscale = 1.0, yscale = 1.0;
		#define PLOT(x,y)  srcTexture[ (int)(x*xscale) + 256*(int)(y*yscale) ] = -1

		xscale = 256.0/MAX_FOG_PLANE;

		if (bFirstTime)
		{
			/* allocate texture first time */
			TRY( kmCreateTextureSurface( &ChartSurfaceDesc, 256, 256, KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565 ) );
			bFirstTime = FALSE;
		}

		/* clear it to black */
		memset( srcTexture, 0, sizeof(srcTexture) );

		/* draw fog function */
		{
			int firstX, lastX;

			firstX = getWDepthFromIndexAndFogFactor(127, FogFactor);
			lastX = getWDepthFromIndexAndFogFactor(0, FogFactor);

			/* we actually draw every pixel; we could draw just at fog-table sample locations */
			for ( x=firstX; x < lastX; x++ )
			{
				PLOT( x, (int)(120-getDensityAtW(x)*110) );
			}
		}

		/* draw marks at first & last fog control points */
		for (y=10; y < 120; y+=2)
		{
			PLOT( (int)getWDepthFromIndexAndFogFactor(127, FogFactor), y );
			PLOT( (int)getWDepthFromIndexAndFogFactor(0, FogFactor), y );
		}

		/* twiddle and download */
		/* TODO: make sure we aren't overwritting texture as it's being used? */
		TRY( kmuCreateTwiddledTexture( (void*)dstTexture, (void*)srcTexture, 0, 0, KM_MAPSIZE_256, KM_TEXTURE_RGB565 ) );
		TRY( kmLoadTexture( &ChartSurfaceDesc, (void*)dstTexture, 0, 0 ) );
	}



}





				static KMPACKEDARGB FogColors[] =
				{
					RGB(0  ,0  ,0  ),
					RGB(128,128,128),
					RGB(128,0  ,0  ),
					RGB(0  ,128,0  ),
					RGB(0  ,0  ,128),
					RGB(128,128,0  ),
					RGB(128,0  ,128),
					RGB(0  ,128,128),
					RGB(100,50 ,50),
				};
				static int nCurrentFogColor = 0;










void loadSceneTextures( Struct_Scene *pScene )
{

	/*** First attempt at loading multiple textures... */
	{
		int i;
		for (i=0; i < pScene->nTextures; i++)
		{
			TRY( kmCreateTextureSurface( &Surfaces[i], pScene->pTextures[i].nWidth, pScene->pTextures[i].nHeight, /*KM_TEXTURE_TWIDDLED | */KM_TEXTURE_ARGB4444 ) );
			TRY( kmLoadTexture( &Surfaces[i], (void*)pScene->pTextures[i].pBitmapData, 0, 0 ) );
		}
	}

	/* Ugh- we need valid texture to use specular alpha for fog, so create solid white texture for what should be untextured surfaces */
/*	TRY( kmCreateTextureSurface( &Surfaces[pScene->nTextures], 8, 8, KM_TEXTURE_TWIDDLED | KM_TEXTURE_ARGB4444 ) );
	TRY( kmLoadTexture( &Surfaces[pScene->nTextures], (void*)solid_texture_data, 0, 0 ) );
*/
}


void freeSceneTextures( Struct_Scene *pScene )
{
	/*** ASSUME that we've allocated the appropriate textures first... ***/
	/* We should track textures within each scene, instead of globally */

	int i;
	for (i=0; i < pScene->nTextures; i++)
	{
		TRY( kmFreeTexture(&Surfaces[i]) );
	}

/*	TRY( kmFreeTexture(&Surfaces[pScene->nTextures]) );*/
}





/*===========================================================================*/

/* FOG FUNCTIONS */



#define FPU_EXP_BIAS		127


float getWDepthFromIndexAndFogFactor( int index, int factor )
{
	float fFogScale, WDepth;

	fFogScale = getFloatScaleFactorFromHWScaleFactor( factor );
	WDepth = fFogScale / ( pow ( 2.0, index >> 4 ) * (float)(( index & 0x0f ) + 16 ) / 16.0f );
	return WDepth;
}




/*
This function computes a table-fog scale factor that allows the table to have
control up to the specified depth (usually the far clip plane)
*/

int computeFogScaleFactorViaMaxZ( float z )
{
/*
	The furthest table control point is entry[0],
	which provides the density when OOW == 1.0/ScaleFactor.
	We want the ScaleFactor to be close to Z (actually, = or > ).
*/

	/* convert floating point Z into packed mantissa/exponent format for HW */
	int mantissa, exponent;

	union
	{
		float floatVal;
		int   intVal;
	} sharedType;

	sharedType.floatVal = z;


	/* round mantissa up; mantissa overflow will bump exponent */
	sharedType.intVal += 0xffff;
	mantissa = (sharedType.intVal >> 16 & 0x7f) + 0x80;		/* +0x80 is to restore the leading 1 */
	exponent = (sharedType.intVal >> 23 & 0xff) - FPU_EXP_BIAS;

	return (mantissa << 8) + (exponent & 0xff);
}

float getFloatScaleFactorFromHWScaleFactor( int hwFactor )
{
	union
	{
		float floatVal;
		int   intVal;
	} sharedType;

	sharedType.intVal = ( hwFactor + FPU_EXP_BIAS << 23 & 0x7f800000 ) + 	/* FPU exponent */
						( hwFactor << 8 & 0x007f0000 );  /* Mantissa; remove leading 1 */

	return ( sharedType.floatVal );
}


char *menuLineText[] =
{
	"Fog Mode:      ",
	"Fog Function:  ",
	"Fog Color:     ",
	"Fog Far-plane: ",
	"Scene:         ",
};

enum enumMenuLine
{
	MENU_FOGMODE,
	MENU_FOGFUNCTION,
	MENU_FOGCOLOR,
	MENU_FARFOG,
	MENU_SCENE
};


char *menuChoiceText[sizeof(menuLineText)/sizeof(char *)];



int nMenuLines = sizeof(menuLineText)/sizeof(char *);


int menuValues[ sizeof(menuLineText)/sizeof(char *) ];

char stringFogColor[40];
char stringFogFarValue[40];

char stringSceneName[40];

int nActiveLine = 0;


int wrapWithin( int val, int max )
{
	if ( val < 0 )
		val = max-1;
	if ( val >= max )
		val = 0;
	return val;
}

/* this will basically be our MENU stuff */
void test()
{

	int i;
	PDS_PERIPHERAL *pJoy;
	menuValues[MENU_SCENE] = sceneNum;

	/* we assume somebody else already refreshed this */
	pJoy = pdGetPeripheral(PDD_PORT_A0);

	/* Refresh menu-choice text (all of them for simplicity) */

	sprintf( stringFogColor, "0x%08x", FogColors[menuValues[MENU_FOGCOLOR]] );

	menuChoiceText[MENU_FOGCOLOR] = stringFogColor;
	menuChoiceText[MENU_FOGMODE] = fogModeNames[fogMode];
	menuChoiceText[MENU_FOGFUNCTION] = fogFormulaNames[nFogFormula];
	sprintf( stringFogFarValue, "%d", menuValues[MENU_FARFOG] = fogFar );
	menuChoiceText[MENU_FARFOG] = stringFogFarValue;
	sprintf( stringSceneName, "#%d", menuValues[MENU_SCENE] );
	menuChoiceText[MENU_SCENE] = stringSceneName;


	/* print menu on screen using diagnostic text (simple but slow) */

	diagSetPixelDoubling(1,1); /* use 16x16 font for readability on 640x480 interlace screen */
	diagSetScreenMargin(2);  /* 2 character margin with 16x16 font gives us 32 pixel margin */
	diagSetBGColor(0,0,50);
	diagSetFGColor(240,240,240);
	diagSetXY(0,0);

	for (i=0; i < nMenuLines; i++)
	{
		if ( i == nActiveLine )
			diagSetBGColor(0,0,200);
		else
			diagSetBGColor(0,0,50);

		/* print only first part of string */
        //diagPrintf( "%s", menuLineText[i] );
        //diagPrintf( "%s\n", menuChoiceText[i] );
	}


#if 0   // disabled text menu due to slowdown
	diagPrintf(
        "Full %d Main %d, Part %d\n",
		syTmrGenCountToMicro(last_refresh_loop, SYD_TMR_CLOCK_P64),
		syTmrGenCountToMicro(time_main_loop, SYD_TMR_CLOCK_P64),
		syTmrGenCountToMicro(time_particle_loop, SYD_TMR_CLOCK_P64));

	diagPrintf(
        "Full %d Main %d, Part %d\n",
		syTmrGenCountToMicro(frozen_refresh_loop, SYD_TMR_CLOCK_P64),
		syTmrGenCountToMicro(frozen_main_loop, SYD_TMR_CLOCK_P64),
		syTmrGenCountToMicro(frozen_particle_loop, SYD_TMR_CLOCK_P64));
#endif

	if ( pJoy->press & PDD_DGT_KU )
		if ( --nActiveLine < 0 )
			nActiveLine = nMenuLines-1;

	if ( pJoy->press & PDD_DGT_KD )
		if ( ++nActiveLine >= nMenuLines )
			nActiveLine = 0;

	if ( ( pJoy->press & (PDD_DGT_KL | PDD_DGT_KR) ) ||
		 ( nActiveLine == MENU_FARFOG  &&  pJoy->on & (PDD_DGT_KL | PDD_DGT_KR) ) )
	{
		int incSpeed = 1;
		if ( nActiveLine == MENU_FARFOG )
			incSpeed = 6;

		if ( pJoy->on & PDD_DGT_KL )
			menuValues[nActiveLine] -= incSpeed;
		if ( pJoy->on & PDD_DGT_KR )
			menuValues[nActiveLine] += incSpeed;

		/* And any special mode change code can happen here */
		switch (nActiveLine)
		{
		case MENU_FOGCOLOR:
			menuValues[MENU_FOGCOLOR] = wrapWithin( menuValues[MENU_FOGCOLOR], sizeof(FogColors)/sizeof(KMPACKEDARGB) );
			TRY( kmSetFogVertexColor(FogColors[menuValues[MENU_FOGCOLOR]]) );
			TRY( kmSetFogTableColor(FogColors[menuValues[MENU_FOGCOLOR]]) );
			break;

		case MENU_FOGMODE:
			menuValues[MENU_FOGMODE] = wrapWithin( menuValues[MENU_FOGMODE], eNumFogModes );
			fogMode = menuValues[MENU_FOGMODE];
			SetupContextForFogMode(&context, fogMode);
			SetupContextForFogMode(&blurContext, fogMode);
			break;

		case MENU_FOGFUNCTION:
			menuValues[MENU_FOGFUNCTION] = wrapWithin( menuValues[MENU_FOGFUNCTION], NUM_FOGS );
			nFogFormula = menuValues[MENU_FOGFUNCTION];
			SetupFogTable();
			break;

		case MENU_FARFOG:
			menuValues[MENU_FARFOG] = wrapWithin( menuValues[MENU_FARFOG], MAX_FOG_PLANE );  /* this should have a min also */
			fogFar = menuValues[MENU_FARFOG];
			SetupFogTable();
			break;

		case MENU_SCENE:
			menuValues[MENU_SCENE] = wrapWithin( menuValues[MENU_SCENE], numScenes );  /* this should have a min also */

			/* when changing scenes, change textures... however, we should wait for previous render to finish first! */
			if ( sceneNum != menuValues[MENU_SCENE] )
			{
				freeSceneTextures( allScenes[sceneNum] );
				loadSceneTextures( allScenes[menuValues[MENU_SCENE]] );
			}

			sceneNum = menuValues[MENU_SCENE];
			break;

		default:
			break;
		}
	}

}


/*
Questions:

fog table - manual says use table entries 0 - 127, but 128 has effect too

depth values listed in Kamui manual are wrong- they all all 1/2 the actual value.

does the fogfactor value have a signed exponent? Or unsigned 8 bit value? - 2's complement (signed)

why do equivelant fog factor values yield different results? (ie: 0x20 * 2^0x08 should == 0x40 * 2^0x07 !)
(Guess we require normalized mantissa's with high-bit set)

discuss where to do vertex-fog computation- at projection, or during xform; pros & cons

I really need a SAFE, RELIABLE debugging text-output routine (for asserts, diagnostics, crash-dumps, etc)

TODO: Exporter also spits out -1 for undefined material indices; should kludge in temp one

*/
