
// the "depend" batch doesn't like non-existant headers
#include "global.h"
#include <islpsi.h>
#include <psitypes.h>

#include "main.h"

#include "bff_load.h"
#include "map_draw.h"
#include "maths.h"
#include "debris.h"
#include "dcinc.h"

// Keep in an included .h file, jim
//#ifdef _WINDOWS
//#include "pcHardware3D.h"
//#endif




//#define MAPDRAW_SHOW_VIS_SPHERES
#ifdef DREAMCAST
char pcDCacheData[1024*4];
#define DCACHE  pcDCacheData
#else
#ifndef _WINDOWS
#define DCACHE  0x1f800000
#else
char pcDCacheData[1024*4];
#define DCACHE  pcDCacheData
#endif
#endif	

#ifdef POLY_COUNTERS
int polycounts[N_POLY_COUNTERS];
#endif

extern GsRVIEW2	newcamera;

#ifdef DEBUG
int lscape_just_one_model = -1;
int lscape_jom_ticker = 0;
#endif

#ifdef _WINDOWS
extern int scrOffsetX, scrOffsetY;
extern int runningHard;
#endif

extern void transformVertexList_NoDepth(VERT *vertPtr, long numVerts, long *transformedVerts);
extern void transformVertexListLong(long *p);
extern void transformVertexListA_ZeroCam(long *p);

// Scale-Depth-Down deals with shifting large depth-coordinates in a scaled-up map,
// shifting down to a smaller range of OT positions.

// Scale-Adjust allows a scaled-up map to be integrated into an otherwise NON-scaled-up
// coordinate system (probably a temporary measure), by increasing the object-camera
// distance for the duration of the map-plotting calls.

#define MAP_SCALE_DEPTH_DOWN 3

// Remove the safety check on the landscape model depth check
#define NO_LANDSCAPE_LIMITS


// A couple of the optimisations in here are enablable by "#define". Once this is sourcesafed, it
//may be worth tidying them out to make the source neater.

/*

  Routines to RENDER frogger2 maps.

  Essentially, an optimised model printer that only deals with
  
	Textured Tris & Quads
	Single-Sided only.
	Non-Transparent.

*/




// PSI.C variables rqd for the draw prims.
// They're defined in the latest islpsi header, so that's ok.
//extern long *transformedVertices;
//extern VERT *transformedNormals;
//extern long *transformedDepths;

// I dunno what part of the ISL lib these are in. Part of the psi lib, I think,
// but it's not in the headers yet
/*
// custom.h
typedef struct{
	SHORT x,y,z,w;
}SHORTQUAT;
*/
//void ShortquaternionGetMatrix(SHORTQUAT *squat, MATRIX *dmatrix);
/*
typedef struct
{
	short x,y;
}SHORTXY;
*/



#ifndef ADD2POINTER
	#define ADD2POINTER(a,b) ( (void *) ( (int)(a) + (b))  )
#endif



// "addPrimLen" is faster than doing "setPolyGT3/4","addPrim",
// becuase a fair few of the numbers are already known / filled in

// In fact, IF we can guarantee that we're linking into an OT address (as we can, here),
// and NOT the tail end of another primitive, then the "length" part of the P_TAG will be zero,
// so we can just read/write the OT address as a "u_long" instead of using "getaddr/setaddr"

// The "<<24" is optimised to a constant by the compiler if "len" is constant.

// this version also assumes we've got a spare register for use as "tempvar" to get rid of a NOP

#define addPrimLen(ot,p,len,tempvar) \
{\
	tempvar = *(u_long *)(ot); \
    (*(u_long *)(ot) = (u_long)(p)); \
	tempvar |= (len<<24); \
	*(unsigned long *)(p) = tempvar; \
}


// (Previous, less-optimised, versions
/*
#define addPrimLen(ot,p,len)\
{\
	(*(unsigned long *)p = *(u_long *)(ot) | (len<<24));\
    (*(u_long *)(ot) = (u_long)p);\
}
*/
//#define addPrimLen(ot,p,len)	(*(unsigned long *)p = getaddr(ot) | (len<<24)), setaddr(ot,p)
//#define addPrimLen(ot,p,len)	{setlen(p,len); addPrim(ot,p);}



// ==== JS : These macros are now in PSX_PC.h which is included via custom.h.  Sorry, but I've
// =====     had to do this for the PC conversion.

// Extra bit of direct gte-access that doesn't then store the register into a stacked variable
// (ie, this gives a nice little speed improvement)
/*#define gte_stotz_cpu(r)\
asm(\
  "mfc2 %0, $7;"\
  :\
  : "r" (r)\
)*/
// Fred, checking whether the same speed improvement works with the backface cull.
// (Answer, yes it does. Hope I've got this bit of asm right!)
/*#define gte_stopz_cpu(r)\
asm(\
	"mfc2 %0, $24;"\
	:\
	: "r"(r)\
)*/


// Extra Map Depth is now part of the mesh header. The idea is that it's set to about 10, to ensure that
// the frog appears in front of the landscape. "Underwater" objects should have their extra depth set higher,
// so they don't sort badly with the water plane itself
//#define XTRA_MAP_DEPTH 10


// The MIN map depth is basically in here because when doing the perspective transform
// close to / overlapping the camera plane, the PSX switches to an isometric transform rather than a perspective one,
// which looks horrible!
#define MIN_MAP_DEPTH 10



// Must be under 1024 to work with the library's OT
// Use lower values still to define the far cut-off distance.

// Actually, 1000 * 8, remember... We're clipping objects to 900.
// Add a polygon dimension of 100, and an ickle bit of safety margin
// ta-da. let's say 1050
#define MAX_MAPOBJECT_DIMENSION 1050
// This ends up being somewhere around the 700->800 mark
#ifndef _WINDOWS
#define MAX_MAP_DEPTH_UPPER_LIMIT (( 32768 - MAX_MAPOBJECT_DIMENSION * 8) >> 5)
#else
#define MAX_MAP_DEPTH_UPPER_LIMIT 32768
#endif

int max_map_ot_depth = MAX_MAP_DEPTH_UPPER_LIMIT;

#define MAX_MAP_DEPTH max_map_ot_depth

#ifndef _WINDOWS
#define MAX_MAP_DEPTH_CLIPCHECK 1023
#else
int MAX_MAP_DEPTH_CLIPCHECK = 1023;
#endif


#ifdef DREAMCAST

// *ASL* 04/09/2000 - Dreamcast specific headers

// un-comment to use maximum z check in map draw function
//#define MAPDRAW_USE_MAXZCHECK

extern int				mapPolyCount;

extern KMSTRIPHEAD		StripHead_GT4_Sub;
extern KMSTRIPHEAD		StripHead_GT4_Add;
extern KMSTRIPHEAD		StripHead_GT3_Add;


/* --------------------------------------------------------------------------------
   Structure : TDCWorldMesh
   Purpose : world segment mesh structure for holding pre-compiled polygon lists
   Info : 
*/
typedef struct _TDCWorldMesh
{
	// gouraud shaded textured quads
	int				noofQuadTextPolys;				// number of quad textured polygons
	int				*quadTextIndices;				// quad textured indices
	KMDWORD			*quadTextVerts;					// quad textured vertices pointer
	// gouraud shaded textured triangles
	int				noofTriTextPolys;				// number of tri textured polygons
	int				*triTextIndices;				// quad textured indices
	KMDWORD			*triTextVerts;					// triangle textured vertices pointer
	// gouraud shaded quads
	int				noofQuadPolys;					// number of quad polygons
	int				*quadIndices;					// quad indices
	KMDWORD			*quadVerts;						// quad vertices pointer
	// gouraud shaded triangles
	int				noofTriPolys;					// number of tri polygons
	int				*triIndices;					// quad indices
	KMDWORD			*triVerts;						// triangle vertices pointer

	// vertices
	int				noofVerts;						// number of map vertices
	TDCVector4		*vertices;						// map vertices pointer

	// extra polygon strip headers
	int				noofExtraPolyStrips;			// number of extra polygon strips
	KMSTRIPHEAD		*extraPolyStrips;				// extra polygon strips

} TDCWorldMesh;


// world meshes for current world
static TDCWorldMesh	*dcWorldMeshes = NULL;

// number of meshes in the current world
static int dcNoofWorldMeshes = 0;

static void MapDraw_Dreamcast_DrawMesh(WORLD_MESH_INSTANCE *instance, int depthscaler, TDCWorldMesh *dcMesh);


#endif

// ===================================================
// New Mesh... Non-instanced, two copies of the poly data...
// Removed, because MESH4's are 3 times the size of MESH1's.
// (The speedup's ok, but not good enough to justify that, even on a fraction of the meshes)

/*
#ifndef _WINDOWS
void MapDraw_DrawFMA_Mesh4(WORLD_MESH_INSTANCE *instance, int depthscaler)
{
// "depth" is forced to be a CPU register so we can use the fast gte macro
// (Also note, it's shared with a temporary variable "t1")
	register long clipflag asm("$16");
	register u_long t2 asm("$16");
	register long depth asm("$17");
	register char *opcd asm("$18");
	register void *si asm("$19");

	register long *tfv;
	register long *tfd;

	FMA_MESH_HEADER *mesh = instance->mesh;
	int page = (currentDisplayPage==displayPage)?(0):(1);	// Which one to write to?

// These macris are for an optimisation that requires all our vertex numbers to be multiplied by four,
// which means they're byte offsets into the tfv/tfd table rather than int-array indices
// Essentially
// tfv[n] and tfd[n], where tfv/tfd are arrays of ints, have now been replaced by these macros,
// where "n" is pre-shifted at PC map-compile time. Thus saving a shift instruction per vertex per polygon.
#define GETX(n)( ((SHORTXY *)( (int)(tfv) +(n) ))->x )
#define GETY(n)( ((SHORTXY *)( (int)(tfv) +(n) ))->y )
#define GETV(n)(  *(u_long *)( (int)(tfv) +(n) ) )
#define GETD(n)(  *(u_long *)( (int)(tfd) +(n) ) )

#define si3 ((POLY_GT3 *)(si))
#define si4 ((POLY_GT4 *)(si))

	unsigned long *ot = currentDisplayPage->ot + instance->sort_depth;
	int i;

	int min_depth = MIN_MAP_DEPTH - instance->sort_depth;
	int max_depth = MAX_MAP_DEPTH - instance->sort_depth;

	if(min_depth < 1)
	{
		min_depth = 1;
	}

	if(max_depth > 1024-instance->sort_depth)
		max_depth = 1024-instance->sort_depth;

	if(mesh->n_verts <= 126)
	{
		tfv = (void *)DCACHE;
		tfd = (void *)(DCACHE + 512);
	}
		else if(mesh->n_verts <= 255)	// Yes, 255. TransformVerts rounds up to nearest 3, remember
	{
		tfv = (void *)DCACHE;
		tfd = transformedDepths;
//		tfv = transformedVertices;
//		tfd = DCACHE;
	}
	else
	{
		tfv = transformedVertices;
		tfd = transformedDepths;
	}


	{
		long p[9];
		p[0] = (int)mesh->verts;	// 0
		p[1] = mesh->n_verts;		// 4
		p[2] = (int)tfv;			// 8
		p[3] = (int)tfd;			// 12
		p[4] = instance->posx - mycamera.vpx;	// 16
		p[5] = instance->posy - mycamera.vpy;	// 20
		p[6] = instance->posz - mycamera.vpz;	// 24

		transformVertexListA_ZeroCam(&p[0]);
	}

//#ifdef MAP_SCALE_DEPTH_DOWN
	if(depthscaler)
	{
		for(i = 0; i < mesh->n_verts; i++)
			tfd[i] = tfd[i] >> depthscaler;
	}
//#endif

#define op ((FMA4_GT4 *)opcd)

	op = (FMA4_GT4 *)mesh->gt4s;
	si4 = &op->poly[page];

	for(i = mesh->n_gt4s; i != 0; i--,op++,si4 = ADD2POINTER(si4,sizeof(FMA4_GT4)))
	{
		gte_ldsz4(GETD(op->vert0),GETD(op->vert1),GETD(op->vert2),GETD(op->vert3));
   		gte_avsz4();
		gte_stotz_cpu(depth);
		if((depth > min_depth) && (depth < max_depth))
		{

			if(
			((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert3) & 0xff80ff00) + 0x00800100)
			& 0x01000200
			)
			continue;

//			if(GETX(op->vert0) < -256 && GETX(op->vert1) < -256 && GETX(op->vert2) < -256 && GETX(op->vert3) < -256) continue;
//			if(GETX(op->vert0) >  256 && GETX(op->vert1) >  256 && GETX(op->vert2) >  256 && GETX(op->vert3) >  256) continue;
//			if(GETY(op->vert0) < -128 && GETY(op->vert1) < -128 && GETY(op->vert2) < -128 && GETY(op->vert3) < -128) continue;
//			if(GETY(op->vert0) >  128 && GETY(op->vert1) >  128 && GETY(op->vert2) >  128 && GETY(op->vert3) >  128) continue;

			gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));
			gte_nclip();
			gte_stopz_cpu(clipflag);

			if (clipflag<0)
			{
				INCPOLYCOUNT(MAP_POLYS);

				t2 = *(u_long *) (&GETV(op->vert3));
				gte_stsxy3_gt4(si4);	// The first 3 x's & y's are already in the gte, so we may as well use 'em
				*(u_long *)  (&si4->x3) = t2;
				addPrimLen(ot+(depth), (si4),12,t2);	// the hi (length) byte of the linked list pointer is already 12, but this is prolly quicker
			}
		}
	}

#undef op

// That's the quads done, now let's do the triangles...

#define op ((FMA4_GT3 *)opcd)

	op = (FMA4_GT3 *)mesh->gt3s;
	si3 = &op->poly[page];

	for(i = mesh->n_gt3s; i != 0; i--,op++,si3 = ADD2POINTER(si3,sizeof(FMA4_GT3)))
	{
		gte_ldsz3(GETD(op->vert0),GETD(op->vert1),GETD(op->vert2));
   		gte_avsz3();
		gte_stotz_cpu(depth);

		if(depth > min_depth && depth < max_depth)
		{
			if(
			((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
			& 0x01000200
			)
			continue;

//			if(GETX(op->vert0) < -256 && GETX(op->vert1) < -256 && GETX(op->vert2) < -256) continue;
//			if(GETX(op->vert0) >  256 && GETX(op->vert1) >  256 && GETX(op->vert2) >  256) continue;
//			if(GETY(op->vert0) < -128 && GETY(op->vert1) < -128 && GETY(op->vert2) < -128) continue;
//			if(GETY(op->vert0) >  128 && GETY(op->vert1) >  128 && GETY(op->vert2) >  128) continue;


			gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));
			gte_nclip();
			gte_stopz_cpu(clipflag);
			if(clipflag < 0)
			{
				INCPOLYCOUNT(MAP_POLYS);
				addPrimLen(ot+(depth), (si3),9,t2);	// the hi (length) byte of the linked list pointer is already 9, but this is prolly quicker
				gte_stsxy3_gt3(si3);
			}
		}
	}

#undef op
}

#endif		// #ifndef _WINDOWS
*/
// ===================================================
// New, mega-optimised landscape plotter
// This is the routine to draw a single mesh shape.
// Assumes that the matrix is already set up,
// and that any visibility tests have already been done

// We're working with a normal scaledown of 3
// However we might be doing 1 or 2 scaledowns within the matrix transform for large models
// in which case, reduce the depthscaler here to compensate
#ifndef _WINDOWS
void MapDraw_DrawFMA_Mesh3(WORLD_MESH_INSTANCE *instance, int depthscaler)
{
#ifndef DREAMCAST
#ifndef _WINDOWS
	register u_long t1 asm("$16");	// I've re-jigged the code, so "depth" is re-usable, & shares register with t1
	register u_long t2 asm("$20");

	register PACKET * packet asm("$17");
	register char *opcd asm("$18");
#else
	register u_long t1;	// I've re-jigged the code, so "depth" is re-usable, & shares register with t1
	register u_long t2;

	register PACKET * packet;
	register char *opcd;
#endif

	register long *tfv;
	register long *tfd;

	FMA_MESH_HEADER *mesh = instance->mesh;

#ifdef _WINDOWS
	if (runningHard)
		MAX_MAP_DEPTH_CLIPCHECK = 4096;
	else
		MAX_MAP_DEPTH_CLIPCHECK = 1023;
#endif


// These macris are for an optimisation that requires all our vertex numbers to be multiplied by four,
// which means they're byte offsets into the tfv/tfd table rather than int-array indices
// Essentially
// tfv[n] and tfd[n], where tfv/tfd are arrays of ints, have now been replaced by these macros,
// where "n" is pre-shifted at PC map-compile time. Thus saving a shift instruction per vertex per polygon.
#define GETX(n)( ((SHORTXY *)( (int)(tfv) +(n) ))->x )
#define GETY(n)( ((SHORTXY *)( (int)(tfv) +(n) ))->y )
#define GETV(n)(  *(u_long *)( (int)(tfv) +(n) ) )
#define GETD(n)(  *(u_long *)( (int)(tfd) +(n) ) )


// "depth" is forced to be a CPU register so we can use the fast gte macro
// (Also note, it's shared with a temporary variable "t1")
#ifndef _WINDOWS
	register long clipflag asm("$20");
	register long depth asm("$16");
#else
	register long clipflag;
	register long depth;
#endif

	unsigned long *ot = currentDisplayPage->ot + instance->sort_depth;

	int i;


	int min_depth = MIN_MAP_DEPTH - instance->sort_depth;
	int max_depth = MAX_MAP_DEPTH - instance->sort_depth;


	t1 = t2 = 0;	// CPW: this stops compiler warning
	clipflag = depth = 0;


	if(min_depth < 1)
	{
		min_depth = 1;
	}

	if(max_depth > 1024-instance->sort_depth)
		max_depth = 1024-instance->sort_depth;


	if(mesh->n_verts <= 126)
	{
		tfv = (void *)DCACHE;
		tfd = (void *)(DCACHE + 512);
	}
		else if(mesh->n_verts <= 255)	// Yes, 255. TransformVerts rounds up to nearest 3, remember
	{
		tfv = (void *)DCACHE;
		tfd = transformedDepths;
//		tfv = transformedVertices;
//		tfd = DCACHE;
	}
	else
	{
		tfv = transformedVertices;
		tfd = transformedDepths;
	}


	{
		long p[9];
		p[0] = (int)mesh->verts;	// 0
		p[1] = mesh->n_verts;		// 4
		p[2] = (int)tfv;			// 8
		p[3] = (int)tfd;			// 12
		p[4] = instance->posx - mycamera.vpx;	// 16
		p[5] = instance->posy - mycamera.vpy;	// 20
		p[6] = instance->posz - mycamera.vpz;	// 24
		transformVertexListA_ZeroCam(&p[0]);
	}
//	transformVertexListA_ChickenRun(mesh->verts,mesh->n_verts,tfv,tfd);	// for now. Note that this is gonna relocate everything to the origin

#ifdef DEBUG
	if(lscape_just_one_model != -1 && padData.debounce[4] & PAD_CIRCLE)
	{
		char str[50];
		int v;
		DB("===n verts = %d\n",mesh->n_verts);
		for(v = 0; v < mesh->n_verts; v++)
		{
			DB("xyz = %d %d %d\n",(int)((short)(tfv[v] & 0xffff)), (int)((short)((tfv[v] & 0xffff0000) >> 16)), tfd[v]);
		}
	}
#endif


	//transformVertexListLong(mesh->verts, mesh->n_verts, tfv, tfd);

//#ifdef MAP_SCALE_DEPTH_DOWN
	if(depthscaler)
	{
		for(i = 0; i < mesh->n_verts; i++)
			tfd[i] = tfd[i] >> depthscaler;
	}
//#endif

	packet = (PACKET *)currentDisplayPage->primPtr;


// The MapDraw stores a count+list of quads and one of tris,
// rather than checking a "type" element for every polygon.
// (coz it only has those two types. No transparent. No double sided. No nothing)




#define si ((POLY_GT4*)packet)
#define op ((FMA_GT4 *)opcd)

	op = mesh->gt4s;

// Little note - Down-counters are quicker than up-counters because you can
// compare with zero rather than with a variable.
// "!=0" is also marginally faster than ">0" here.
// n_gt4s CAN be zero. Otherwise,a (do,while) loop would be ever-so-slightly faster.than the (for...) one.

	for(i = mesh->n_gt4s; i != 0; i--,op++)
	{
// Get average Z of the polygon & use as a depth pointer
		gte_ldsz4(GETD(op->vert0),GETD(op->vert1),GETD(op->vert2),GETD(op->vert3));
   		gte_avsz4();
		gte_stotz_cpu(depth);
		if(depth > min_depth && depth < max_depth)
		{
// Skip the poly if all the verts are off screen.
// A fairly hefty check, but one that gives us a 20% code-speed increase & 30% less gpu work.
//			if ( (GETX(op->vert0)+256)&(GETX(op->vert1)+256)&(GETX(op->vert2)+256)&(GETX(op->vert3)+256)&512) continue;
//			if ( (GETY(op->vert0)+128)&(GETY(op->vert1)+128)&(GETY(op->vert2)+128)&(GETY(op->vert3)+128)&256) continue;

// Here's an entertaining little optimisation for the above that does X & Y at the same time,
// masking off the low bits to avoid overflow problems, and knocks a further 10% off the code side of things
// (Looking at the source, the compiler has sufficient registers free for two of the three constants.
// Hence the speedup)
			if(
			((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert3) & 0xff80ff00) + 0x00800100)
			& 0x01000200
			)
			continue;

//			if(GETX(op->vert0) < -256 && GETX(op->vert1) < -256 && GETX(op->vert2) < -256 && GETX(op->vert3) < -256) continue;
//			if(GETX(op->vert0) >  256 && GETX(op->vert1) >  256 && GETX(op->vert2) >  256 && GETX(op->vert3) >  256) continue;
//			if(GETY(op->vert0) < -128 && GETY(op->vert1) < -128 && GETY(op->vert2) < -128 && GETY(op->vert3) < -128) continue;
//			if(GETY(op->vert0) >  128 && GETY(op->vert1) >  128 && GETY(op->vert2) >  128 && GETY(op->vert3) >  128) continue;

// Load 1st three vertices for a backface cull check
// (Yes, this check is better when placed after the offscreen one - Checked on PA)
			gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));
			gte_nclip();
			gte_stopz_cpu(clipflag);

			if (clipflag<0)
			{
				INCPOLYCOUNT(MAP_POLYS);


// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
				addPrimLen(ot+(depth), (si),12,t2);
// Copy the polygon data over into the OT
				t1 = *(u_long *) (&op->u0);
				t2 = *(u_long *) (&op->u1);
				*(u_long *)  (&si->u0) = t1;
				*(u_long *)  (&si->u1) = t2;
				gte_stsxy3_gt4(si);	// The first 3 x's & y's are already in the gte, so we may as well use 'em
				t1 = *(u_long *) (&op->u2);
				t2 = *(u_long *) (&op->u3);
				*(u_long *)  (&si->u2) = t1;
				*(u_long *)  (&si->u3) = t2;
				*(u_long *)  (&si->x3) = *(u_long *) (&GETV(op->vert3));
				t1 = *(u_long *) (&op->r0);
				t2 = *(u_long *) (&op->r1);
				*(u_long *)  (&si->r0) = t1;
				*(u_long *)  (&si->r1) = t2;
				t1 = *(u_long *) (&op->r2);
				t2 = *(u_long *) (&op->r3);
				*(u_long *)  (&si->r2) = t1;
				*(u_long *)  (&si->r3) = t2;
				packet = ADD2POINTER(packet,sizeof(POLY_GT4));
			}
		}
	}

#undef si
#undef op

// That's the quads done, now let's do the triangles...

#define si ((POLY_GT3*)packet)
#define op ((FMA_GT3 *)opcd)

	op = mesh->gt3s;
	for(i = mesh->n_gt3s; i != 0; i--,op++)
	{
// Get average Z of the polygon & use as a depth pointer
		gte_ldsz3(GETD(op->vert0),GETD(op->vert1),GETD(op->vert2));
   		gte_avsz3();
		gte_stotz_cpu(depth);

		if(depth > min_depth && depth < max_depth)
		{

// Skip the poly if all the verts are off screen.
// A fairly hefty check, but one that gives us a 20% code-speed increase & 30% less gpu work.

//			if ( (GETX(op->vert0)+256)&(GETX(op->vert1)+256)&(GETX(op->vert2)+256)&512) continue;
//			if ( (GETY(op->vert0)+128)&(GETY(op->vert1)+128)&(GETY(op->vert2)+128)&256) continue;

// Optimisation - X & Y at the same time
			if(
			((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
			& 0x01000200
			)
			continue;

//			if(GETX(op->vert0) < -256 && GETX(op->vert1) < -256 && GETX(op->vert2) < -256) continue;
//			if(GETX(op->vert0) >  256 && GETX(op->vert1) >  256 && GETX(op->vert2) >  256) continue;
//			if(GETY(op->vert0) < -128 && GETY(op->vert1) < -128 && GETY(op->vert2) < -128) continue;
//			if(GETY(op->vert0) >  128 && GETY(op->vert1) >  128 && GETY(op->vert2) >  128) continue;

// Load 1st three vertices for a backface cull
			gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));
			gte_nclip();
			gte_stopz_cpu(clipflag);
			if(clipflag < 0)
			{
				INCPOLYCOUNT(MAP_POLYS);
// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
				addPrimLen(ot+(depth), (si),9,t2);
// Copy the polygon data over into the OT
				t1 = *(u_long *) (&op->u0);
				t2 = *(u_long *) (&op->u1);
				*(u_long *)  (&si->u0) = t1;
				*(u_long *)  (&si->u1) = t2;
				gte_stsxy3_gt3(si);
				t1 = *(u_long *) (&op->u2);
				t2 = *(u_long *) (&op->r0);
				*(u_long *)  (&si->u2) = t1;
				*(u_long *)  (&si->r0) = t2;
				t1 = *(u_long *) (&op->r1);
				t2 = *(u_long *) (&op->r2);
				*(u_long *)  (&si->r1) = t1;
				*(u_long *)  (&si->r2) = t2;
				packet = ADD2POINTER(packet,sizeof(POLY_GT3));
			}
		}
	}

#undef si
#undef op
//#undef xy

// New & improved landscape models can now have built-in skies...

#define si ((POLY_G4*)packet)
#define op ((FMA_G4 *)opcd)

	op = mesh->g4s;

	for(i = mesh->n_g4s; i != 0; i--,op++)
	{
		gte_ldsz4(GETD(op->vert0),GETD(op->vert1),GETD(op->vert2),GETD(op->vert3));
   		gte_avsz4();
		gte_stotz_cpu(depth);

		if(depth > min_depth && depth < max_depth)
		{
			if(
			((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert3) & 0xff80ff00) + 0x00800100)
			& 0x01000200
			)
			continue;

	// For sky models, backface cull is still necessary.
	// (coz of polys behind you)
	// depth is always right at the back

			gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));

			gte_nclip();
			gte_stopz_cpu(clipflag);
			if (clipflag<0)
			{
				INCPOLYCOUNT(MAP_POLYS);
		// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
				addPrimLen(ot+ (depth), (si),8,t2);
		// Copy the polygon data over into the OT

				t1 = *(u_long *) (&op->r0);
				t2 = *(u_long *) (&op->r1);
				*(u_long *)  (&si->r0) = t1;
				*(u_long *)  (&si->r1) = t2;
				gte_stsxy3_g4(si);	// The first 3 x's & y's are already in the gte, so we may as well use 'em
				t1 = *(u_long *) (&op->r2);
				t2 = *(u_long *) (&op->r3);
				*(u_long *)  (&si->r2) = t1;
				*(u_long *)  (&si->r3) = t2;
				*(u_long *)  (&si->x3) = *(u_long *) (&GETV(op->vert3));

				packet = ADD2POINTER(packet,sizeof(POLY_G4));
			}
		}
	}

#undef si
#undef op

// That's the quads done, now let's do the triangles...

#define si ((POLY_G3*)packet)
#define op ((FMA_G3 *)opcd)

	op = mesh->g3s;
	for(i = mesh->n_g3s; i != 0; i--,op++)
	{
		gte_ldsz3(GETD(op->vert0),GETD(op->vert1),GETD(op->vert2));
   		gte_avsz3();
		gte_stotz_cpu(depth);

		if(depth > min_depth && depth < max_depth)
		{
// Optimisation - X & Y at the same time
			if(
			((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
			&
			((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
			& 0x01000200
			)
			continue;

			gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));
			gte_nclip();
			gte_stopz_cpu(clipflag);

			if (clipflag<0)
			{
				INCPOLYCOUNT(MAP_POLYS);
		// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
				addPrimLen(ot+(depth), (si),6,t2);
		// Copy the polygon data over into the OT
				t2 = *(u_long *) (&op->r0);
				*(u_long *)  (&si->r0) = t2;
				gte_stsxy3_g3(si);
				t1 = *(u_long *) (&op->r1);
				t2 = *(u_long *) (&op->r2);
				*(u_long *)  (&si->r1) = t1;
				*(u_long *)  (&si->r2) = t2;
				packet = ADD2POINTER(packet,sizeof(POLY_G3));
			}
		}
	}

#undef si
#undef op



	currentDisplayPage->primPtr = (char *)packet;
#endif
}

#endif		// #ifndef _WINDOWS





// Set up the PSX's transformation matrix for a landscape segment,
// or possibly for a NON-ROTATING platform.
void MapDraw_SetMatrix(WORLD_MESH_INSTANCE *instance, int shifts)
{
	MATRIX tx;

	tx.t[0] = 0;
	tx.t[1] = 0;
	tx.t[2] = 0;
	if(shifts != 3)
	{
		shifts = 3 - shifts;
		tx.m[0][0] = GsWSMATRIX.m[0][0] >> shifts;
		tx.m[0][1] = GsWSMATRIX.m[0][1] >> shifts;
		tx.m[0][2] = GsWSMATRIX.m[0][2] >> shifts;
		tx.m[1][0] = GsWSMATRIX.m[1][0] >> shifts;
		tx.m[1][1] = GsWSMATRIX.m[1][1] >> shifts;
		tx.m[1][2] = GsWSMATRIX.m[1][2] >> shifts;
		tx.m[2][0] = GsWSMATRIX.m[2][0] >> shifts;
		tx.m[2][1] = GsWSMATRIX.m[2][1] >> shifts;
		tx.m[2][2] = GsWSMATRIX.m[2][2] >> shifts;
		gte_SetRotMatrix(&tx);
	}
	else
	{
		gte_SetRotMatrix(&GsWSMATRIX);
	}

	gte_SetTransMatrix(&tx);

}

// =========================== Untextured Polygon Sky Mesh Version ====================================



void MapDraw_SetSkyMatrix(FMA_MESH_HEADER *mesh)
{
	MATRIX tx;

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

// Unnecessary maths for landscape segments, where pos is always zero.
//	gte_ldlvl( &mesh->posx);
//	gte_rtirtr();
//	gte_stlvl(&tx.t);
	tx.t[0] = 0;
	tx.t[1] = 0;
	tx.t[2] = 0;

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&tx);

}

void MapDraw_DrawSkyMesh(FMA_SKYMESH_HEADER *mesh)
{
#ifndef DREAMCAST
// Note - t1 & t2 are shared with other variables
#ifndef _WINDOWS
	register u_long t1 asm("$16");
	register u_long t2 asm("$20");

	register PACKET * packet asm("$17");
	register char *opcd asm("$18");

	register long *tfv = transformedVertices;
#else
	register u_long t1;
	register u_long t2;

	register PACKET * packet;
	register char *opcd;

	register long *tfv = transformedVertices;
#endif

#define GETV(n)(  *(u_long *)( (int)(tfv) +(n) ) )

	unsigned long *ot = currentDisplayPage->ot + 1023;	// right at the back of the OT...

	int i;
#ifndef _WINDOWS
	register long clipflag asm("$20");
#else
	register long clipflag;
#endif

	t1 = t2 = clipflag = 0;

//	transformVertexListA(mesh->verts, mesh->n_verts, tfv, tfd);
	transformVertexList_NoDepth(mesh->verts, mesh->n_verts, tfv);

	packet = (PACKET *)currentDisplayPage->primPtr;

#define si ((POLY_G4*)packet)
#define op ((FMA_G4 *)opcd)

	op = mesh->g4s;

	for(i = mesh->n_g4s; i != 0; i--,op++)
	{

		if(
		((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
		&
		((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
		&
		((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
		&
		((GETV(op->vert3) & 0xff80ff00) + 0x00800100)
		& 0x01000200
		)
		continue;

// For sky models, backface cull is still necessary.
// (coz of polys behind you)
// depth is always right at the back

		gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));

		gte_nclip();
		gte_stopz_cpu(clipflag);
		if (clipflag<0)
		{
			INCPOLYCOUNT(SKY_POLYS);
	// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
			addPrimLen(ot, (si),8,t2);
	// Copy the polygon data over into the OT

			t1 = *(u_long *) (&op->r0);
			t2 = *(u_long *) (&op->r1);
			*(u_long *)  (&si->r0) = t1;
			*(u_long *)  (&si->r1) = t2;
			gte_stsxy3_g4(si);	// The first 3 x's & y's are already in the gte, so we may as well use 'em
			t1 = *(u_long *) (&op->r2);
			t2 = *(u_long *) (&op->r3);
			*(u_long *)  (&si->r2) = t1;
			*(u_long *)  (&si->r3) = t2;
			*(u_long *)  (&si->x3) = *(u_long *) (&GETV(op->vert3));

			packet = ADD2POINTER(packet,sizeof(POLY_G4));
		}
	}

#undef si
#undef op

// That's the quads done, now let's do the triangles...

#define si ((POLY_G3*)packet)
#define op ((FMA_G3 *)opcd)

	op = mesh->g3s;
	for(i = mesh->n_g3s; i != 0; i--,op++)
	{
// Optimisation - X & Y at the same time
		if(
		((GETV(op->vert0) & 0xff80ff00) + 0x00800100)
		&
		((GETV(op->vert1) & 0xff80ff00) + 0x00800100)
		&
		((GETV(op->vert2) & 0xff80ff00) + 0x00800100)
		& 0x01000200
		)
		continue;

		gte_ldsxy3(GETV(op->vert0), GETV(op->vert1), GETV(op->vert2));
		gte_nclip();
		gte_stopz_cpu(clipflag);

		if (clipflag<0)
		{
			INCPOLYCOUNT(SKY_POLYS);

	// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
			addPrimLen(ot, (si),6,t2);
	// Copy the polygon data over into the OT
			t2 = *(u_long *) (&op->r0);
			*(u_long *)  (&si->r0) = t2;
			gte_stsxy3_g3(si);
			t1 = *(u_long *) (&op->r1);
			t2 = *(u_long *) (&op->r2);
			*(u_long *)  (&si->r1) = t1;
			*(u_long *)  (&si->r2) = t2;
			packet = ADD2POINTER(packet,sizeof(POLY_G3));
		}
	}

#undef si
#undef op
//#undef xy

	currentDisplayPage->primPtr = (char *)packet;
#endif
}


#define OFFLEFT		1
#define OFFRIGHT	2
#define OFFUP		4
#define OFFDOWN		8
#define OFFFRONT	16
#define OFFBACK		32

// Check to see if a frogger map shape is on-screen
// (Assumes the projection matrix has already been set up for this shape)
// Normal value for shifts = 3

typedef struct MAPDRAW_CLIPCHECKSTR
{
	long	p[7];
	SVECTOR Box[9];
	long verts[9];
	long depths[9];
}MAPDRAW_CLIPCHECKSTR;

int MapDraw_ClipCheck(WORLD_MESH_INSTANCE *instance, int shifts)
{
	ULONG check,totalchecks;
	SHORTXY			*xy;
	int v,i;
	MAPDRAW_CLIPCHECKSTR *checkstr = (MAPDRAW_CLIPCHECKSTR *)(DCACHE);
	FMA_MESH_HEADER *mesh = instance->mesh;
	int nearobject;


// Get the 8 corners of the object's bounding box, & transform them.
	for(v=0,i=0; v<8; v++)
	{
		checkstr->Box[i].vx=(v&1)?mesh->minx:mesh->maxx;
		checkstr->Box[i].vy=(v&2)?mesh->miny:mesh->maxy;
		checkstr->Box[i].vz=(v&4)?mesh->minz:mesh->maxz;
		i++;
	}
// We're transforming 9 verts anyway, so we may as well make use of that, and
// tx the box centre
	checkstr->Box[i].vx=(mesh->minx + mesh->maxx) >>1 ;
	checkstr->Box[i].vy=(mesh->miny + mesh->maxy) >>1;
	checkstr->Box[i].vz=(mesh->minz + mesh->maxz) >>1;

	checkstr->p[0]=(long)(&checkstr->Box[0]);
	checkstr->p[1]=9;
	checkstr->p[2]=(long)&checkstr->verts[0];
	checkstr->p[3]=(long)&checkstr->depths[0];

// correct version
	checkstr->p[4] = instance->posx - mapCamData.gameCam.vpx;	// 16
	checkstr->p[5] = instance->posy - mapCamData.gameCam.vpy;	// 20
	checkstr->p[6] = instance->posz - mapCamData.gameCam.vpz;	// 24

	transformVertexListA_ZeroCam(&checkstr->p[0]);

// Check to see whether ALL of the vertices are on the outisde of ANY of the 6 planes of the view-frustrum
	(ULONG*)xy=&checkstr->verts;
	totalchecks=0xffff;
	nearobject = 0;
	for(v=0; v<8; v++)
	{
		check=0;

#ifdef DREAMCAST
		if(xy[v].x<0)
			check|=OFFLEFT;
		if(xy[v].x>640 )
			check|=OFFRIGHT;
		if(xy[v].y<0)
			check|=OFFUP;
		if(xy[v].y>480 )
			check|=OFFDOWN;
#else
#ifndef _WINDOWS
		if(xy[v].x<-256)
			check|=OFFLEFT;
		if(xy[v].x>256 )
			check|=OFFRIGHT;
		if(xy[v].y<-128)
			check|=OFFUP;
		if(xy[v].y>128 )
			check|=OFFDOWN;
#else
		if(xy[v].x + scrOffsetX < 0)
			check|=OFFLEFT;
		if(xy[v].x + scrOffsetX > scrOffsetX*2)
			check|=OFFRIGHT;
		if(xy[v].y + scrOffsetY < 0)
			check|=OFFUP;
		if(xy[v].y + scrOffsetY > scrOffsetY*2)
			check|=OFFDOWN;
#endif
#endif

#ifndef _WINDOWS	// =========================================================================

#ifdef MAP_SCALE_DEPTH_DOWN
	  	if((checkstr->depths[v]) <= (MIN_MAP_DEPTH<<(2+shifts)))
			check|=OFFFRONT;

		if((checkstr->depths[v]) >= (MAX_MAP_DEPTH_CLIPCHECK<<(2+shifts)))
			check |= OFFBACK;
#else
	  	if((checkstr->depths[v]) <= (MIN_MAP_DEPTH<<2))
			check|=OFFFRONT;

		if((checkstr->depths[v]) >= (MAX_MAP_DEPTH_CLIPCHECK<<2))
			check |= OFFBACK;
#endif
		if(checkstr->depths[v] == 0)
			nearobject  = 1;

#else	// =========================================================================

#ifdef MAP_SCALE_DEPTH_DOWN
	  	if((checkstr->depths[v]) <= (MIN_MAP_DEPTH << 3))
			check|=OFFFRONT;

		if((checkstr->depths[v]) >= (MAX_MAP_DEPTH_CLIPCHECK << 3))
			check |= OFFBACK;
#else
	  	if((checkstr->depths[v]) <= (MIN_MAP_DEPTH << 3))
			check|=OFFFRONT;

		if((checkstr->depths[v]) >= (MAX_MAP_DEPTH_CLIPCHECK << 3))
			check |= OFFBACK;
#endif
		if(checkstr->depths[v] <= 0)
			nearobject  = 1;
#endif	// =========================================================================

		totalchecks&=check;
	}

// There's an additional test, which is if the centrepoint's behind us, but there's a corner that's managed to wrap to "way in front"
	if(nearobject || checkstr->depths[8] <= (MAX_MAP_DEPTH << (2+shifts)) / 4)
	{
		for(v=0; v<8; v++)
		{
			if((checkstr->depths[v]) >= ((MAX_MAP_DEPTH_CLIPCHECK<<(2+shifts))) * 3 / 4)
			{
//				utilPrintf("spanning\n");
				return 0;
			}
		}
	}

	return(totalchecks==0);
}

WORLD_MESH_INSTANCE *mapdrawFindInstance(FMA_WORLD2 *world, int tag)
{
	int i;
	WORLD_MESH_INSTANCE *entry;

	entry = ADD2POINTER(world,sizeof(FMA_WORLD2));
	for(i = world->n_meshes; i != 0; i--, entry++)
	{
		if(entry->tag == tag)
		{
			return entry;
		}
	}
	return NULL;
}

void MapDraw_DrawFMA_World(FMA_WORLD2 *world)
{
#ifdef DREAMCAST
	// *ASL* 05/09/2000 - hold our dc mesh
	TDCWorldMesh			*dcWorldMesh;
#endif
	int i;
//	int removed_models;
	FMA_MESH_HEADER **mesh;
//	FMA_WORLDENTRY *entry;
	WORLD_MESH_INSTANCE *entry;

	if(world->id != BFF_FMA_WORLD2_ID)
	{
		printf("Only World2's supported now!\n");
		CRASH;
	}

#ifdef _WINDOWS
	if (runningHard)
		MAX_MAP_DEPTH_CLIPCHECK = 9028;
	else
		MAX_MAP_DEPTH_CLIPCHECK = 1023;
#endif


// Quick bit of last-minute insurance...
#ifdef NO_LANDSCAPE_LIMITS
	if(max_map_ot_depth > MAX_MAP_DEPTH_UPPER_LIMIT)
	{
		static int warned = 0;
		if(!warned)
		{
			utilPrintf(" *** WARNING ***\n");
			utilPrintf(" Map-Printing depth has been set to a possibly overflowing value\n");
			utilPrintf(" Current value %d. Max safe %d\n",max_map_ot_depth,MAX_MAP_DEPTH_UPPER_LIMIT);

			warned = 1;
		}
	}
	if(max_map_ot_depth > 1023)
	{
		max_map_ot_depth = 1023;
	}

#else
	if(max_map_ot_depth > MAX_MAP_DEPTH_UPPER_LIMIT)
		max_map_ot_depth = MAX_MAP_DEPTH_UPPER_LIMIT;
#endif

	entry = ADD2POINTER(world,sizeof(FMA_WORLD));


#ifdef DEBUG
	lscape_jom_ticker++;
	if((padData.debounce[4] & PAD_RIGHT) || ((padData.digital[4] & PAD_RIGHT) && lscape_jom_ticker > 5))
	{
		lscape_jom_ticker = 0;
		lscape_just_one_model++;
		if(lscape_just_one_model >= world->n_meshes)
			lscape_just_one_model = 0;
	}
	else if((padData.debounce[4] & PAD_LEFT) || ((padData.digital[4] & PAD_LEFT) && lscape_jom_ticker > 5))
	{
		lscape_jom_ticker = 0;
		lscape_just_one_model--;
		if(lscape_just_one_model <0)
			lscape_just_one_model = world->n_meshes-1;
	}
	else if(lscape_jom_ticker >= 30 * 6)
	{
		lscape_jom_ticker = 0;
		lscape_just_one_model = -1;
	}
	if(lscape_just_one_model != -1)
	{
		char str[50];
		sprintf(str,"Mesh %d/%d",lscape_just_one_model,world->n_meshes);
		fontPrint(font, 0,100,str, 128,128,64);

		sprintf(str,"x y z %d %d %d",
			entry[lscape_just_one_model].posx/8,
			-entry[lscape_just_one_model].posy/8,
			entry[lscape_just_one_model].posz/8
			);
		fontPrint(font, 0,112,str, 128,128,64);

	}


#endif

	
/*
	{
		int other;
		FMA_MESH_HEADER *mesh1;
		FMA_MESH_HEADER *mesh2;
		WORLD_MESH_INSTANCE *entry2;
			
		for(i = 0; i < world->n_meshes; i++, entry++)
		{
			mesh1 = entry->mesh;

			entry2 = ADD2POINTER(world,sizeof(FMA_WORLD));
			for(other = 0; other < i; other++,entry2++)
			{
				mesh2 = entry2->mesh;

				if(mesh2 == mesh1)
				{
					DB("Instance mesh CRC = &%8x\n",mesh2->name_crc);
					ASSERT(entry->mesh->id != BFF_FMA_MESH4_ID);
				}
			}
		}

		entry = ADD2POINTER(world,sizeof(FMA_WORLD));
	}
*/








#ifdef DEBUG
	for(i = 0; i < world->n_meshes; i++, entry++)
	{
		if(entry->enabled && (lscape_just_one_model == -1 || lscape_just_one_model == i))
#else
	for(i = world->n_meshes; i != 0; i--, entry++)
	{
		if(entry->enabled)
#endif
		{
			int dx,dy,dz;
			unsigned int m = 0;

			dx = entry->posx - mycamera.vpx;
			dy = entry->posy - mycamera.vpy;
			dz = entry->posz - mycamera.vpz;


			if(   dx > -0x7fff && dx < 0x7fff
			   && dy > -0x7fff && dy < 0x7fff
			   && dz > -0x7fff && dz < 0x7fff
				)
			{
				int shifts;

				shifts = 3;
// changing shifts on the matrix size doesn't affect the clipping prob
// (points still overflow)
/*
				if(m > 0x3fff * 0x3fff)
				{
					if(m > 0x1fff * 0x1fff)
					{
						shifts = 1;
					}
					else
					{
						shifts = 2;
					}
				}
				else
				{
					shifts = 3;
				}
*/
#ifdef DEBUG
				if(lscape_just_one_model == i)
				{
					utilPrintf("m=%d, dxyz = %d %d %d\n",m,dx,dy,dz);
				}
#endif
				{
					MapDraw_SetMatrix(entry,shifts);
#ifdef DREAMCAST
					// *ASL* 04/09/2000 - ..Then set mine
					{
						VECTOR v;
						v.vx = TransVector.vx;
						v.vy = TransVector.vy;
						v.vz = TransVector.vz;			// add on depth bump to offset the map where needed
						SH4XD_SetMatrix(&RotMatrix, &v);
					}
#endif

 					if(MapDraw_ClipCheck(entry,shifts))
					{
#ifdef _WINDOWS
						if (runningHard)
							PCH3D_MapDraw_DrawFMA_Mesh3(entry,3);
						else
							MapDraw_DrawFMA_Mesh3(entry,3);	// shifts);
#else

#ifdef DREAMCAST
						// *ASL* 07/09/2000 - Draw our dc world map
						if (dcNoofWorldMeshes > 0 && dcWorldMeshes != NULL)
						{
#ifdef DEBUG
							dcWorldMesh = &dcWorldMeshes[i];
#else
							dcWorldMesh = &dcWorldMeshes[dcNoofWorldMeshes-i];
#endif
							MapDraw_Dreamcast_DrawMesh(entry, shifts, dcWorldMesh);
						}
						else
							MapDraw_DrawFMA_Mesh3DC(entry, shifts);


#else
/*
						if(entry->mesh->id == BFF_FMA_MESH4_ID)
						{
							MapDraw_DrawFMA_Mesh4(entry,shifts);	// Fred, testing for speedups
						}
						else
*/
						{
							MapDraw_DrawFMA_Mesh3(entry,shifts);
						}
#endif
					  

#endif
					}
				}
			}
		}
	}
}


// ===================================================
//              Platform-related code
// Note that this is ALL kept after the map code, in
// order that the map code scrunches itself into the
// instruction cache.
// (Ideally, this file should be 4K aligned in the
// linker)
// ===================================================

// Set up the PSX's transformation matrix for a platform segment,
// Calculating the rot-matrix is, of course, only neccesary if the thing's rotated
// In fact, it might be nicer to cache this thing somewhere rather than recalculating it all the time
void MapDraw_SetPlatformMatrix(WORLD_MESH_INSTANCE *instance)	//FMA_MESH_HEADER *mesh)
{
	MATRIX tx;
	MATRIX tx1;
	FMA_MESH_HEADER *mesh = instance->mesh;

// If we're using quaternions, get the matrix from (xyzw) this way
//	ShortquaternionGetMatrix((SHORTQUAT *)(&mesh->rotx), &tx1);

// If we're using xyz angles, get the matrix from (xyz) this way
	RotMatrixYXZ_gte((SVECTOR *)&instance->rotx,&tx1);


	gte_MulMatrix0(&GsWSMATRIX,&tx1,&tx);

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

	{
		VECTOR temp;
		temp.vx = instance->posx - mycamera.vpx;
		temp.vy = instance->posy - mycamera.vpy;
		temp.vz = instance->posz - mycamera.vpz;
		gte_ldlvl(&temp);
		gte_rtirtr();
		gte_stlvl(&tx.t);
	}

	instance->posx = mycamera.vpx;	// instance->pos is done as a pre-rotation add to coords
	instance->posy = mycamera.vpy;
	instance->posz = mycamera.vpz;

//	tx.t[0] = 0;
//	tx.t[1] = 0;
//	tx.t[2] = 0;

	gte_SetRotMatrix(&tx);
	gte_SetTransMatrix(&tx);
}


// This is what to call to clip and draw a moving platform
void MapDraw_DrawFMA_Mesh(WORLD_MESH_INSTANCE *instance)
{
	int dx,dy,dz;
	int m;
	int radius = instance->radius;	// was 1000
	FMA_MESH_HEADER *mesh = instance->mesh;



	dx = instance->posx - mycamera.vpx;
	dy = instance->posy - mycamera.vpy;
	dz = instance->posz - mycamera.vpz;
	if(   dx > -0x7fff && dx < 0x7fff
	   && dy > -0x7fff && dy < 0x7fff
	   && dz > -0x7fff && dz < 0x7fff
		)
	{
		m = dx * dx + dy * dy + dz * dz; 
		if(m < (0x7fff * 0x7fff))
		{
			if ((utilSqrt(m) >> 16) + radius < 0x7fff)
			{
				MapDraw_SetPlatformMatrix(instance);

				if(MapDraw_ClipCheck(instance,3))
				{
#ifdef _WINDOWS
					if (runningHard)
						PCH3D_MapDraw_DrawFMA_Mesh3(instance,3);
					else
						MapDraw_DrawFMA_Mesh3(instance,3);
#else
#ifdef DREAMCAST
						MapDraw_DrawFMA_Mesh3DC(instance,3);
#else
						MapDraw_DrawFMA_Mesh3(instance,3);
#endif
#endif
				}

				instance->posx = mycamera.vpx + dx;	// fix the instance pos, because setplatformmatrix zeroes it
				instance->posy = mycamera.vpy + dy;
				instance->posz = mycamera.vpz + dz;

			}
		}
	}
}












void MapDraw_Draw_SkyWorld(FMA_WORLD *world)
{
	int i;
	FMA_MESH_HEADER **mesh;
	mesh = ADD2POINTER(world,sizeof(FMA_WORLD));

	for(i = world->n_meshes; i != 0; i--, mesh++)
	{
		MapDraw_SetSkyMatrix(*mesh);

//		if(MapDraw_ClipCheckS(*mesh))
		{
#ifdef DREAMCAST
			MapDraw_DrawSkyMeshDC((void *)*mesh);
#else
#ifndef _WINDOWS
			MapDraw_DrawSkyMesh((void *)*mesh);
#else
			PC3DH_MapDraw_DrawSkyMesh((void *)*mesh);
#endif
#endif
		}
	}
}


MAPCAMDATATYPE mapCamData;

void mapCamSetRefView2L(GsRVIEW2 *pView)
{
#ifdef DEBUG
	{
		static int ohead = 0;
		if(padData.debounce[4] & PAD_UP)
		{
			if(ohead < 3)
				ohead++;
		}
		if(padData.debounce[4] & PAD_DOWN)
		{
			if(ohead > -1)
				ohead--;
		}


		switch(ohead)
		{
		case -1:
			pView->vpy = -500;
			break;
		case 1:
			pView->vpy = -5000;
			pView->vpx = (pView->vpx + pView->vrx)/2;
			pView->vpz = (pView->vpz + pView->vrz)/2;
			break;
		case 2:
			pView->vpy = -10000;
			break;
		case 3:
			pView->vpy = -12000;
			break;
		}
	}
#endif
	// make a copy of requested camera position, so other routines can refer to it later
	COPYVECTOR((VECTOR *)&(mapCamData.gameCam.vpx), (VECTOR *)&(pView->vpx));
	COPYVECTOR((VECTOR *)&(mapCamData.gameCam.vrx), (VECTOR *)&(pView->vrx));
	mapCamData.realCam.rz=pView->rz;
	mapCamData.realCam.super=pView->super;

// work out camera position
// (p's the position, r's where you're pointing at, dude)
	SUBVECTOR((VECTOR *)&(mapCamData.realCam.vrx), (VECTOR *)&(pView->vrx), (VECTOR *)&(pView->vpx));
	ZEROVECTOR((VECTOR *)&(mapCamData.realCam.vpx));

	mapCamData.realCam.rz=pView->rz;
	mapCamData.realCam.super=pView->super;

	// set it
	GsSetRefView2L(&mapCamData.realCam);
}

// Sphere visibility check.
// (Adapted from Glover)
#ifdef _WINDOWS
extern int scrOffsetX, scrOffsetY;
#endif
int mapdrawIsSphereVisible(VECTOR *vec, int radius)
{
	VERT vect;
	int	gteH;
	int temp;

	SHORT xy[2];
	LONG distancescale;
	LONG spritez;

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);


	temp = vec->vx - mapCamData.gameCam.vpx;
	if(temp < -0x7fff || temp > 0x7fff)
		return 0;
	vect.vx=temp;
	temp = vec->vz - mapCamData.gameCam.vpz;
	if(temp < -0x7fff || temp > 0x7fff)
		return 0;
	vect.vz=temp;
	temp = vec->vy - mapCamData.gameCam.vpy;
	if(temp < -0x7fff || temp > 0x7fff)
		return 0;
	vect.vy=temp;

	gte_ldv0(&vect);
	gte_rtps();
	gte_stsxy(&xy[0]);

	gte_stszotz(&spritez);

	if(spritez <= 0)
		return 0;

	gte_ReadGeomScreen(&gteH);
	distancescale = (radius * gteH) / spritez / 4;

#ifdef MAPDRAW_SHOW_VIS_SPHERES
	overlayShowSphere(vec->vx, vec->vy, vec->vz, radius, 0x00ffff);
#endif

//	printf("x,y,d = %d %d %d\n",xy[0],xy[1],distancescale);

// actually, this never happens... you just get 1024's in the x/y
#ifndef _WINDOWS
	if(distancescale < 4)
	{
//		printf("Radcheck %d %d %d Tiny\n", xy[0],xy[1],distancescale);
		return 0;
	}
#ifdef DREAMCAST
	if(xy[0]+distancescale < 0 || xy[0] - distancescale > 640)	// tbd - check "resolution"
#else
	if(xy[0]+distancescale < -256 || xy[0] - distancescale > 256)	// tbd - check "resolution"
#endif
	{
//		printf("Radcheck %d %d %d Off H\n", xy[0],xy[1],distancescale);
		return 0;
	}
#ifdef DREAMCAST
	if(xy[1]+distancescale < 0 || xy[1] - distancescale > 480)
#else
	if(xy[1]+distancescale < -128 || xy[1] - distancescale > 128)
#endif
	{
//		printf("Radcheck %d %d %d Off V\n", xy[0],xy[1],distancescale);
		return 0;
	}
#else
	if(distancescale < 4)
	{
//		printf("Radcheck %d %d %d Tiny\n", xy[0],xy[1],distancescale);
		return 0;
	}
	if(xy[0]+distancescale < -scrOffsetX || xy[0] - distancescale > scrOffsetX)	// tbd - check "resolution"
	{
//		printf("Radcheck %d %d %d Off H\n", xy[0],xy[1],distancescale);
		return 0;
	}
	if(xy[1]+distancescale < -scrOffsetY || xy[1] - distancescale > scrOffsetY)
	{
//		printf("Radcheck %d %d %d Off V\n", xy[0],xy[1],distancescale);
		return 0;
	}
#endif
//	printf("** Radcheck %d %d %d on\n", xy[0],xy[1],distancescale);

	return 1;
}









/////////////////////////////LOTS OF DC STUFF BELOW HERE/////////////////////////////////





#ifdef DREAMCAST
// *ASL* 04/09/2000

/* ---------------------------------------------------------
   Function : MapDraw_Dreamcast_InitWorld
   Purpose : pre-compile WORLD_MESH_INSTANCE dreamcast polygons
   Parameters : WORLD_MESH_INSTANCE array pointer, noof entries
   Returns : 
   Info : 
*/
void MapDraw_Dreamcast_InitWorld(WORLD_MESH_INSTANCE *worldMeshInsts, int noofInsts)
{
	FMA_MESH_HEADER		*mesh;
	FMA_GT4				*psxQuadTexts;
	FMA_GT3				*psxTriTexts;
	FMA_G4				*psxQuads;
	FMA_G3				*psxTris;
	VERT				*psxVerts;
	KMDWORD				*vp;
	TDCVector4			*verts;
	TextureType			*map;
	int					i, j, *ip, noofExtras;
	float				tw,th;


	debug_printf(" ** Initialising Dreamcast Map **\n");

	// allocate our current world mesh array
	if ((dcNoofWorldMeshes = noofInsts) == 0)
		return;
	dcWorldMeshes = (TDCWorldMesh *)MALLOC0(dcNoofWorldMeshes * sizeof(TDCWorldMesh));

	// pre-compile all world instance meshes
	for (i=0; i<noofInsts; i++)
	{
		// load mesh from world instance
		mesh = worldMeshInsts[i].mesh;


		// ** Convert all SVECTOR vertices

		// allocate aligned memory for mesh vertices - (allow a possible extra 2 entries for the interleaved transform)
		dcWorldMeshes[i].noofVerts = mesh->n_verts;
		dcWorldMeshes[i].vertices = (TDCVector4 *)MALLOC0((((mesh->n_verts +1) &-2) +1) * sizeof(TDCVector4));

		// convert vertices to TDCVectors
		verts = dcWorldMeshes[i].vertices;
		for (j=0, psxVerts = mesh->verts; j < mesh->n_verts; j++, psxVerts++)
		{
			verts->x = (float)psxVerts->vx;
			verts->y = (float)psxVerts->vy;
			verts->z = (float)psxVerts->vz;
			verts->w = 1.0f;

			verts++;
		}


		// ** As additive semi-transparent polygons require us to use an additive strip along with
		// ** a pre-generated texture strip.. we should pre-generate the additive strip to save doing
		// ** this within the main map draw loop.

		// count up all the extra texture strips we need
		noofExtras = 0;
		for (j=0, psxQuadTexts = mesh->gt4s; j<mesh->n_gt4s; j++, psxQuadTexts++)
			if (psxQuadTexts->code & 2)
			{
				noofExtras++;
			}
		for (j=0, psxTriTexts = mesh->gt3s; j<mesh->n_gt3s; j++, psxTriTexts++)
			if (psxTriTexts->code & 2)
			{
				noofExtras++;
			}

		// allocate our required extra strips
		dcWorldMeshes[i].noofExtraPolyStrips = noofExtras;
		if (noofExtras == 0)
			dcWorldMeshes[i].extraPolyStrips = NULL;
		else
		{
			dcWorldMeshes[i].extraPolyStrips = (KMSTRIPHEAD *)MALLOC0(noofExtras * sizeof(KMSTRIPHEAD));

			debug_printf(" Adding %d extra additive strips for map %d/%d\n", noofExtras, i, noofInsts);
		}

		// use the count as an index
		noofExtras = 0;


		// ** Pre-compile all Quad Textured Polygon Vertices

		// allocate vertices
		dcWorldMeshes[i].noofQuadTextPolys = mesh->n_gt4s;
		dcWorldMeshes[i].quadTextVerts = (KMDWORD *)MALLOC0((mesh->n_gt4s * 4) * 3 * sizeof(KMDWORD));

		// allocate indices and strip header
		dcWorldMeshes[i].quadTextIndices = (int *)MALLOC0((mesh->n_gt4s * 8) * sizeof(int));

		// compile vertices - 4verts in 1 32byte cache line
		vp = dcWorldMeshes[i].quadTextVerts;
		ip = dcWorldMeshes[i].quadTextIndices;
		for (j=0, psxQuadTexts = mesh->gt4s; j<mesh->n_gt4s; j++, psxQuadTexts++)
		{
			// load this quads texture size
			tw = (float)DCKtextureList[psxQuadTexts->tpage].w;
			th = (float)DCKtextureList[psxQuadTexts->tpage].h;

			// vertex0
			*vp++ = (KMDWORD)RGBA(psxQuadTexts->r0, psxQuadTexts->g0, psxQuadTexts->b0, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->v0 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->u0 / tw; vp++;
			// vertex1
			*vp++ = (KMDWORD)RGBA(psxQuadTexts->r1, psxQuadTexts->g1, psxQuadTexts->b1, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->v1 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->u1 / tw; vp++;
			// vertex2
			*vp++ = (KMDWORD)RGBA(psxQuadTexts->r2, psxQuadTexts->g2, psxQuadTexts->b2, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->v2 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->u2 / tw; vp++;
			// vertex3
			*vp++ = (KMDWORD)RGBA(psxQuadTexts->r3, psxQuadTexts->g3, psxQuadTexts->b3, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->v3 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxQuadTexts->u3 / tw; vp++;

			// is this an additive semi-transparent polygon?
			if (psxQuadTexts->code & 2)
			{
				// ** Additive polygon found. Therefore we need to copy the strip that this polygons
				// ** texture references and change it into an additive strip.

				// copy an empty additive strip into our extra buffer
				if(psxQuadTexts->clut & 64)
					memcpy(&dcWorldMeshes[i].extraPolyStrips[noofExtras], &StripHead_GT4_Sub, sizeof(StripHead_GT4_Sub));
				else
					memcpy(&dcWorldMeshes[i].extraPolyStrips[noofExtras], &StripHead_GT4_Add, sizeof(StripHead_GT4_Add));
				// fill this strip with this polygons texture
				kmChangeStripTextureSurface(&dcWorldMeshes[i].extraPolyStrips[noofExtras], KM_IMAGE_PARAM1, DCKtextureList[psxQuadTexts->tpage].surfacePtr);

				// save this strip header
				ip[3] = (int)&dcWorldMeshes[i].extraPolyStrips[noofExtras];

				// acknowledge the extra strip
				noofExtras++;
			}
			else
			{
				// polygon indices, strip header and texture map.. 1 32byte cache line
				map = &DCKtextureList[psxQuadTexts->tpage];
				ip[3] = (int)&map->stripHead;
			}

			// ** These indices directly need to reference directly into our transform vertices list whoose
			// ** entries are 16bytes in size (xf,yf,zf,wf). Since they are already pre-multiplied by 4 for
			// ** the PSX, we need to multiply them again by 4.

			ip[4] = psxQuadTexts->vert3 << 2;
			ip[5] = psxQuadTexts->vert2 << 2;
			ip[6] = psxQuadTexts->vert1 << 2;
			ip[7] = psxQuadTexts->vert0 << 2;
			ip += 8;
		}


		// ** Pre-compile all Triangle Textured Polygon Vertices

		// allocate vertices
		dcWorldMeshes[i].noofTriTextPolys = mesh->n_gt3s;
		dcWorldMeshes[i].triTextVerts = (KMDWORD *)MALLOC0((mesh->n_gt3s * 3) * 3 * sizeof(KMDWORD));
		// allocate indices and strip header
		dcWorldMeshes[i].triTextIndices = (int *)MALLOC0((mesh->n_gt3s * 8) * sizeof(int));

		// compile vertices
		vp = dcWorldMeshes[i].triTextVerts;
		ip = dcWorldMeshes[i].triTextIndices;
		for (j=0, psxTriTexts = mesh->gt3s; j < mesh->n_gt3s; j++, psxTriTexts++)
		{
			// load this tris texture size
			tw = (float)DCKtextureList[psxTriTexts->tpage].w;
			th = (float)DCKtextureList[psxTriTexts->tpage].h;

			// vertex0
			*vp++ = (KMDWORD)RGBA(psxTriTexts->r0, psxTriTexts->g0, psxTriTexts->b0, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxTriTexts->v0 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxTriTexts->u0 / tw; vp++;
			// vertex1
			*vp++ = (KMDWORD)RGBA(psxTriTexts->r1, psxTriTexts->g1, psxTriTexts->b1, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxTriTexts->v1 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxTriTexts->u1 / tw; vp++;
			// vertex2
			*vp++ = (KMDWORD)RGBA(psxTriTexts->r2, psxTriTexts->g2, psxTriTexts->b2, 255);
			*((PKMFLOAT)vp) = (KMFLOAT)psxTriTexts->v2 / th; vp++;
			*((PKMFLOAT)vp) = (KMFLOAT)psxTriTexts->u2 / tw; vp++;

			// is this an additive semi-transparent polygon?
			if (psxTriTexts->code & 2)
			{
				// copy an empty additive strip into our extra buffer
//				if(psxTriTexts->clut & 64)
//					memcpy(&dcWorldMeshes[i].extraPolyStrips[noofExtras], &StripHead_GT3_Sub, sizeof(StripHead_GT3_Sub));
//				else
					memcpy(&dcWorldMeshes[i].extraPolyStrips[noofExtras], &StripHead_GT3_Add, sizeof(StripHead_GT3_Add));
				// fill this strip with this polygons texture
				kmChangeStripTextureSurface(&dcWorldMeshes[i].extraPolyStrips[noofExtras], KM_IMAGE_PARAM1, DCKtextureList[psxTriTexts->tpage].surfacePtr);

				// save this strip header
				ip[4] = (int)&dcWorldMeshes[i].extraPolyStrips[noofExtras];

				// acknowledge the extra strip
				noofExtras++;
			}
			else
			{
				// polygon indices, strip header and texture map.. 1 32byte cache line
				map = &DCKtextureList[psxTriTexts->tpage];
				ip[4] = (int)&map->stripHead;
			}

			// indices
			ip[5] = psxTriTexts->vert2 << 2;
			ip[6] = psxTriTexts->vert1 << 2;
			ip[7] = psxTriTexts->vert0 << 2;
			ip += 8;
		}


		// ** Pre-compile all Quad Non-Textured Polygon Vertices

		// allocate vertices
		dcWorldMeshes[i].noofQuadPolys = mesh->n_g4s;
		dcWorldMeshes[i].quadVerts = (KMDWORD *)MALLOC0((mesh->n_g4s * 4) * 1 * sizeof(KMDWORD));

		// allocate indices and strip header
		dcWorldMeshes[i].quadIndices = (int *)MALLOC0((mesh->n_g4s * 8) * sizeof(int));

		// compile vertices - 4verts in 1 32byte cache line
		vp = dcWorldMeshes[i].quadVerts;
		ip = dcWorldMeshes[i].quadIndices;
		for (j=0, psxQuads = mesh->g4s; j<mesh->n_g4s; j++, psxQuads++)
		{
			// vertex0
			*vp++ = (KMDWORD)RGBA(psxQuads->r0, psxQuads->g0, psxQuads->b0, 255);
			// vertex1
			*vp++ = (KMDWORD)RGBA(psxQuads->r1, psxQuads->g1, psxQuads->b1, 255);
			// vertex2
			*vp++ = (KMDWORD)RGBA(psxQuads->r2, psxQuads->g2, psxQuads->b2, 255);
			// vertex3
			*vp++ = (KMDWORD)RGBA(psxQuads->r3, psxQuads->g3, psxQuads->b3, 255);

			// polygon indices, strip header and texture map.. 1 32byte cache line
			ip[4] = psxQuads->vert3 << 2;
			ip[5] = psxQuads->vert2 << 2;
			ip[6] = psxQuads->vert1 << 2;
			ip[7] = psxQuads->vert0 << 2;
			ip += 8;
		}


		// ** Pre-compile all Triangle Non-Textured Polygon Vertices

		// allocate vertices
		dcWorldMeshes[i].noofTriPolys = mesh->n_g3s;
		dcWorldMeshes[i].triVerts = (KMDWORD *)MALLOC0((mesh->n_g3s * 3) * 1 * sizeof(KMDWORD));

		// allocate indices and strip header
		dcWorldMeshes[i].triIndices = (int *)MALLOC0((mesh->n_g3s * 8) * sizeof(int));

		// compile vertices
		vp = dcWorldMeshes[i].triVerts;
		ip = dcWorldMeshes[i].triIndices;
		for (j=0, psxTris = mesh->g3s; j<mesh->n_g3s; j++, psxTris++)
		{
			// vertex0
			*vp++ = (KMDWORD)RGBA(psxTris->r0, psxTris->g0, psxTris->b0, 255);
			// vertex1
			*vp++ = (KMDWORD)RGBA(psxTris->r1, psxTris->g1, psxTris->b1, 255);
			// vertex2
			*vp++ = (KMDWORD)RGBA(psxTris->r2, psxTris->g2, psxTris->b2, 255);

			// polygon indices, strip header and texture map.. 1 32byte cache line
			ip[5] = psxTris->vert2 << 2;
			ip[6] = psxTris->vert1 << 2;
			ip[7] = psxTris->vert0 << 2;
			ip += 8;
		}
	}
}


/* ---------------------------------------------------------
   Function : MapDraw_Dreamcast_ReleaseWorld
   Purpose : release the pre-compiled dreamcast world
   Parameters :
   Returns : 
   Info : 
*/

void MapDraw_Dreamcast_ReleaseWorld()
{
	if (dcWorldMeshes != NULL)
	{
		while (dcNoofWorldMeshes--)
		{
			// release mesh vertices
			if (dcWorldMeshes[dcNoofWorldMeshes].vertices != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].vertices);
			}
			// release extra polygon strips
			if (dcWorldMeshes[dcNoofWorldMeshes].extraPolyStrips != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].extraPolyStrips);
			}
			// release textured quads
			if (dcWorldMeshes[dcNoofWorldMeshes].quadTextIndices != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].quadTextIndices);
			}
			if (dcWorldMeshes[dcNoofWorldMeshes].quadTextVerts != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].quadTextVerts);
			}
			// release textured triangles
			if (dcWorldMeshes[dcNoofWorldMeshes].triTextIndices != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].triTextIndices);
			}
			if (dcWorldMeshes[dcNoofWorldMeshes].triTextVerts != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].triTextVerts);
			}
			// release non-textured quads
			if (dcWorldMeshes[dcNoofWorldMeshes].quadIndices != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].quadIndices);
			}
			if (dcWorldMeshes[dcNoofWorldMeshes].quadVerts != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].quadVerts);
			}
			// release non-textured triangles
			if (dcWorldMeshes[dcNoofWorldMeshes].triIndices != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].triIndices);
			}
			if (dcWorldMeshes[dcNoofWorldMeshes].triVerts != NULL)
			{
				FREE(dcWorldMeshes[dcNoofWorldMeshes].triVerts);
			}
		}
		FREE(dcWorldMeshes);
	}
	dcNoofWorldMeshes = 0;
	dcWorldMeshes = NULL;
}


/* ---------------------------------------------------------
   Function : MapDraw_Dreamcast_DrawMesh
   Purpose : draw dreamcast pre-compiled world segment mesh
   Parameters : FMA_MESH_HEADER structure pointer, pre-compiled dreamcast mesh poiinter
   Returns : 
   Info : 
*/

static void MapDraw_Dreamcast_DrawMesh(WORLD_MESH_INSTANCE *instance, int depthscaler, TDCWorldMesh *dcMesh)
{
	float	mapmin;
#ifdef MAPDRAW_USE_MAXZCHECK
	float	mapmax;
#endif
	float	x0, x1, x2, x3;
	float	y0, y1, y2, y3;
	float	z0, z1, z2, z3;

	// calculate polygon maximum and minimum z depths
	mapmin = MIN_MAP_DEPTH - ((float)instance->sort_depth);
	mapmin = MAX(mapmin, 1.0f);
#ifdef MAPDRAW_USE_MAXZCHECK
	mapmax = MAX_MAP_DEPTH - ((float)instance->sort_depth);
	mapmax = MIN(mapmax, (1024.0f - ((float)instance->sort_depth)));
#endif

	// transform this meshes vertices
	{
		float meshOffs[4];
		meshOffs[0] = instance->posx - mycamera.vpx;
		meshOffs[1] = instance->posy - mycamera.vpy;
		meshOffs[2] = instance->posz - mycamera.vpz;
		meshOffs[3] = (float)depthBump;
		SH4XD_TransformVertices(dcMesh->vertices, alignedTransformedVertices, dcMesh->noofVerts, meshOffs);
	}

	// push all map world polygons to KAMUI..
	kmxxGetCurrentPtr(&vertexBufferDesc);
	{
		// integer registers
		register int		tvs = (int)alignedTransformedVertices;
		register int		*vis;
		register int		r0;
		register float		*v0, *v1, *v2, *v3;
		register int		*kmvs;
		register int		*visEnd;
		register int		shLast;

		// float registers
		register float		zmin, fr0, fr1;
#ifdef MAPDRAW_USE_MAXZCHECK
		register float		zmax;
#endif
		// force first strip set
		shLast = 0;


		// ** Push all textured quads

		// prefetch our first polygon..
		if (dcMesh->quadTextIndices)
			prefetch(dcMesh->quadTextIndices);

		// set quad minimum and maximum z clip.. 1/(4*4)
		zmin = mapmin * 16.0f;
#ifdef MAPDRAW_USE_MAXZCHECK
		zmax = mapmax * 16.0f;
#endif
		// push all quads..
		kmvs = (int *)dcMesh->quadTextVerts;
		visEnd = dcMesh->quadTextIndices + (dcMesh->noofQuadTextPolys * 8);
		for (vis = dcMesh->quadTextIndices + 8; vis <= visEnd; vis += 13)
		{
			// prefetch the next polygon
			prefetch(vis);

			// load all this polygons z's
			r0 = *--vis;										// z0
			v0 = (float *)((int)tvs + r0);
			fr0 = *v0;

			r0 = *--vis;										// z1
			v1 = (float *)((int)tvs + r0);
			fr1 = *v1;
			fr0 += fr1;

			r0 = *--vis;										// z2
			v2 = (float *)((int)tvs + r0);
			fr1 = *v2;
			fr0 += fr1;

			r0 = *--vis;										// z3
			v3 = (float *)((int)tvs + r0);
			fr1 = *v3;
			fr0 += fr1;											// z0+z1+z2+z3

			// load ahead this polygons texture map
			r0 = *--vis;

			// z clip check
#ifdef MAPDRAW_USE_MAXZCHECK
			if (fr0 < zmin || fr0 > zmax)
#else
			if (fr0 < zmin)
#endif
			{
				kmvs += 12;
				continue;
			}

			// inc all vertices
			v0++;
			v1++;
			v2++;
			v3++;

			// load all 2d x's
			x0 = *v0++;
			x1 = *v1++;
			x2 = *v2++;
			x3 = *v3++;

			// completely off at the LHS or RHS?
			if ((x0 > 640.0f && x1 > 640.0f && x2 > 640.0f && x3 > 640.0f) ||
				(x0 <   0.0f && x1 <   0.0f && x2 <   0.0f && x3 <   0.0f))
			{
				kmvs += 12;
				continue;
			}

			// load all 2d y's
			y0 = *v0++;
			y1 = *v1++;
			y2 = *v2++;
			y3 = *v3++;

			// completely off at the TOP or BOTTOM?
			if ((y0 > 480.0f && y1 > 480.0f && y2 > 480.0f && y3 > 480.0f) ||
				(y0 <   0.0f && y1 <   0.0f && y2 <   0.0f && y3 <   0.0f))
			{
				kmvs += 12;
				continue;
			}

			// ** Print the polygon

			// load all 1/z's
			z0 = *v0;
			z1 = *v1;
			z2 = *v2;
			z3 = *v3;

			// need to change the texture strip header?
			if (r0 != shLast)
			{
				// change render state
				kmxxStartStrip(&vertexBufferDesc, r0);
				shLast = r0;
			}

			// vertex 0
			r0 = *kmvs++;										// ->rgb
			pkmCurrentPtr += 8;
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z0;
			*(PKMFLOAT)--pkmCurrentPtr = y0;
			*(PKMFLOAT)--pkmCurrentPtr = x0;
			*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8+8;

			// vertex 1
			r0 = *kmvs++;										// ->rgb
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z1;
			*(PKMFLOAT)--pkmCurrentPtr = y1;
			*(PKMFLOAT)--pkmCurrentPtr = x1;
			*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8+8;

			// vertex 2
			r0 = *kmvs++;										// ->rgb
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z2;
			*(PKMFLOAT)--pkmCurrentPtr = y2;
			*(PKMFLOAT)--pkmCurrentPtr = x2;
			*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8+8;

			// vertex 3
			r0 = *kmvs++;										// ->rgb
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z3;
			*(PKMFLOAT)--pkmCurrentPtr = y3;
			*(PKMFLOAT)--pkmCurrentPtr = x3;
			*--pkmCurrentPtr = KM_VERTEXPARAM_ENDOFSTRIP;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8;
		}


		// ** Push all textured triangles

		// prefetch our first polygon..
		if (dcMesh->triTextIndices)
			prefetch(dcMesh->triTextIndices);

		// set triangle minimum and maximum z clip.. 1/(3*4)
		zmin = mapmin * 12.0f;
#ifdef MAPDRAW_USE_MAXZCHECK
		zmax = mapmax * 12.0f;
#endif

		// push all triangles..
		kmvs = (int *)dcMesh->triTextVerts;
		visEnd = dcMesh->triTextIndices + (dcMesh->noofTriTextPolys * 8);
		for (vis = dcMesh->triTextIndices + 8; vis <= visEnd; vis += 12)
		{
			// prefetch the next polygon
			prefetch(vis);

			// load all this polygons z's
			r0 = *--vis;										// z0
			v0 = (float *)((int)tvs + r0);
			fr0 = *v0;

			r0 = *--vis;										// z1
			v1 = (float *)((int)tvs + r0);
			fr1 = *v1;
			fr0 += fr1;

			r0 = *--vis;										// z2
			v2 = (float *)((int)tvs + r0);
			fr1 = *v2;
			fr0 += fr1;											// z0+z1+z2

			// load ahead this polygons texture map
			r0 = *--vis;

			// z clip check
#ifdef MAPDRAW_USE_MAXZCHECK
			if (fr0 < zmin || fr0 > zmax)
#else
			if (fr0 < zmin)
#endif
			{
				kmvs += 9;
				continue;
			}

			// inc all vertices
			v0++;
			v1++;
			v2++;

			// load all 2d x's
			x0 = *v0++;
			x1 = *v1++;
			x2 = *v2++;

			// completely off at the LHS or RHS?
			if ((x0 > 640.0f && x1 > 640.0f && x2 > 640.0f) ||
				(x0 <   0.0f && x1 <   0.0f && x2 <   0.0f))
			{
				kmvs += 9;
				continue;
			}

			// load all 2d y's
			y0 = *v0++;
			y1 = *v1++;
			y2 = *v2++;

			// completely off at the TOP or BOTTOM?
			if ((y0 > 480.0f && y1 > 480.0f && y2 > 480.0f) ||
				(y0 <   0.0f && y1 <   0.0f && y2 <   0.0f))
			{
				kmvs += 9;
				continue;
			}

			// ** Print the polygon

			// load all 1/z's
			z0 = *v0;
			z1 = *v1;
			z2 = *v2;

			// need to change the texture strip header?
			if (r0 != shLast)
			{
				// change render state
				kmxxStartStrip(&vertexBufferDesc, r0);
				shLast = r0;
			}

			// vertex 0
			r0 = *kmvs++;										// ->rgb
			pkmCurrentPtr += 8;
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z0;
			*(PKMFLOAT)--pkmCurrentPtr = y0;
			*(PKMFLOAT)--pkmCurrentPtr = x0;
			*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8+8;

			// vertex 1
			r0 = *kmvs++;										// ->rgb
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z1;
			*(PKMFLOAT)--pkmCurrentPtr = y1;
			*(PKMFLOAT)--pkmCurrentPtr = x1;
			*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8+8;

			// vertex 2
			r0 = *kmvs++;										// ->rgb
			*--pkmCurrentPtr = r0;
			*--pkmCurrentPtr = r0;
			fr0 = *((float *)kmvs); kmvs++;						// ->v
			fr1 = *((float *)kmvs); kmvs++;						// ->u
			*(PKMFLOAT)--pkmCurrentPtr = fr0;
			*(PKMFLOAT)--pkmCurrentPtr = fr1;
			*(PKMFLOAT)--pkmCurrentPtr = z2;
			*(PKMFLOAT)--pkmCurrentPtr = y2;
			*(PKMFLOAT)--pkmCurrentPtr = x2;
			*--pkmCurrentPtr = KM_VERTEXPARAM_ENDOFSTRIP;
			prefetch((void *)pkmCurrentPtr);
			pkmCurrentPtr += 8;
		}


		// ** Now let us deal with the non-tetxured polygons

		// do we need to send any non-textured polygons?
		if (dcMesh->noofQuadPolys > 0 || dcMesh->noofTriPolys > 0)
		{
			// ** As only opaque gouraud shaded polygons are used in the map draw, we can
			// ** set a fixed strip header for all quads and tris
			
			// set gouraud shading strip header
			kmxxStartStrip(&vertexBufferDesc, &StripHead_G4);


			// ** Push all non-textured quads

			// prefetch our first polygon..
			if (dcMesh->quadIndices)
				prefetch(dcMesh->quadIndices);

			// set quad minimum and maximum z clip.. 1/(4*4)
			zmin = mapmin * 16.0f;
#ifdef MAPDRAW_USE_MAXZCHECK
			zmax = mapmax * 16.0f;
#endif
			// push all quads..
			kmvs = (int *)dcMesh->quadVerts;
			visEnd = dcMesh->quadIndices + (dcMesh->noofQuadPolys * 8);
			for (vis = dcMesh->quadIndices + 8; vis <= visEnd; vis += 12)
			{
				// prefetch the next polygon
				prefetch(vis);

				// load all this polygons z's
				r0 = *--vis;										// z0
				v0 = (float *)((int)tvs + r0);
				fr0 = *v0;

				r0 = *--vis;										// z1
				v1 = (float *)((int)tvs + r0);
				fr1 = *v1;
				fr0 += fr1;

				r0 = *--vis;										// z2
				v2 = (float *)((int)tvs + r0);
				fr1 = *v2;
				fr0 += fr1;

				r0 = *--vis;										// z3
				v3 = (float *)((int)tvs + r0);
				fr1 = *v3;
				fr0 += fr1;											// z0+z1+z2+z3

				// z clip check
#ifdef MAPDRAW_USE_MAXZCHECK
				if (fr0 < zmin || fr0 > zmax)
#else
				if (fr0 < zmin)
#endif
				{
					kmvs += 4;
					continue;
				}

				// inc all vertices
				v0++;
				v1++;
				v2++;
				v3++;

				// load all 2d x's
				x0 = *v0++;
				x1 = *v1++;
				x2 = *v2++;
				x3 = *v3++;

				// completely off at the LHS or RHS?
				if ((x0 > 640.0f && x1 > 640.0f && x2 > 640.0f && x3 > 640.0f) ||
					(x0 <   0.0f && x1 <   0.0f && x2 <   0.0f && x3 <   0.0f))
				{
					kmvs += 4;
					continue;
				}

				// load all 2d y's
				y0 = *v0++;
				y1 = *v1++;
				y2 = *v2++;
				y3 = *v3++;

				// completely off at the TOP or BOTTOM?
				if ((y0 > 480.0f && y1 > 480.0f && y2 > 480.0f && y3 > 480.0f) ||
					(y0 <   0.0f && y1 <   0.0f && y2 <   0.0f && y3 <   0.0f))
				{
					kmvs += 4;
					continue;
				}

				// ** Print the polygon

				// load all 1/z's
				z0 = *v0;
				z1 = *v1;
				z2 = *v2;
				z3 = *v3;

				r0 = *kmvs++;										// ->rgb
				pkmCurrentPtr += 7;
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z0;
				*(PKMFLOAT)--pkmCurrentPtr = y0;
				*(PKMFLOAT)--pkmCurrentPtr = x0;
				*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8+7;

				r0 = *kmvs++;										// ->rgb
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z1;
				*(PKMFLOAT)--pkmCurrentPtr = y1;
				*(PKMFLOAT)--pkmCurrentPtr = x1;
				*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8+7;

				r0 = *kmvs++;										// ->rgb
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z2;
				*(PKMFLOAT)--pkmCurrentPtr = y2;
				*(PKMFLOAT)--pkmCurrentPtr = x2;
				*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8+7;

				r0 = *kmvs++;										// ->rgb
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z3;
				*(PKMFLOAT)--pkmCurrentPtr = y3;
				*(PKMFLOAT)--pkmCurrentPtr = x3;
				*--pkmCurrentPtr = KM_VERTEXPARAM_ENDOFSTRIP;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8;
			}


			// ** Push all non-textured triangles

			// prefetch our first polygon..
			if (dcMesh->triIndices)
				prefetch(dcMesh->triIndices);

			// set triangle minimum and maximum z clip.. 1/(3*4)
			zmin = mapmin * 12.0f;
#ifdef MAPDRAW_USE_MAXZCHECK
			zmax = mapmax * 12.0f;
#endif
			// push all triangles..
			kmvs = (int *)dcMesh->triVerts;
			visEnd = dcMesh->triIndices + (dcMesh->noofTriPolys * 8);
			for (vis = dcMesh->triIndices + 8; vis <= visEnd; vis += 11)
			{
				// prefetch the next polygon
				prefetch(vis);

				// load all this polygons z's
				r0 = *--vis;										// z0
				v0 = (float *)((int)tvs + r0);
				fr0 = *v0;

				r0 = *--vis;										// z1
				v1 = (float *)((int)tvs + r0);
				fr1 = *v1;
				fr0 += fr1;

				r0 = *--vis;										// z2
				v2 = (float *)((int)tvs + r0);
				fr1 = *v2;
				fr0 += fr1;											// z0+z1+z2

				// z clip check
#ifdef MAPDRAW_USE_MAXZCHECK
				if (fr0 < zmin || fr0 > zmax)
#else
				if (fr0 < zmin)
#endif
				{
					kmvs += 3;
					continue;
				}

				// inc all vertices
				v0++;
				v1++;
				v2++;

				// load all 2d x's
				x0 = *v0++;
				x1 = *v1++;
				x2 = *v2++;

				// completely off at the LHS or RHS?
				if ((x0 > 640.0f && x1 > 640.0f && x2 > 640.0f) ||
					(x0 <   0.0f && x1 <   0.0f && x2 <   0.0f))
				{
					kmvs += 3;
					continue;
				}

				// load all 2d y's
				y0 = *v0++;
				y1 = *v1++;
				y2 = *v2++;

				// completely off at the TOP or BOTTOM?
				if ((y0 > 480.0f && y1 > 480.0f && y2 > 480.0f) ||
					(y0 <   0.0f && y1 <   0.0f && y2 <   0.0f))
				{
					kmvs += 3;
					continue;
				}

				// ** Print the polygon

				// load all 1/z's
				z0 = *v0;
				z1 = *v1;
				z2 = *v2;

				r0 = *kmvs++;										// ->rgb
				pkmCurrentPtr += 7;
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z0;
				*(PKMFLOAT)--pkmCurrentPtr = y0;
				*(PKMFLOAT)--pkmCurrentPtr = x0;
				*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8+7;

				r0 = *kmvs++;										// ->rgb
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z1;
				*(PKMFLOAT)--pkmCurrentPtr = y1;
				*(PKMFLOAT)--pkmCurrentPtr = x1;
				*--pkmCurrentPtr = KM_VERTEXPARAM_NORMAL;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8+7;

				r0 = *kmvs++;										// ->rgb
				*--pkmCurrentPtr = r0;
				pkmCurrentPtr -= 2;
				*(PKMFLOAT)--pkmCurrentPtr = z2;
				*(PKMFLOAT)--pkmCurrentPtr = y2;
				*(PKMFLOAT)--pkmCurrentPtr = x2;
				*--pkmCurrentPtr = KM_VERTEXPARAM_ENDOFSTRIP;
				prefetch((void *)pkmCurrentPtr);
				pkmCurrentPtr += 8;
			}
		}
	}
	kmxxReleaseCurrentPtr(&vertexBufferDesc);
}

#endif


