

// *ASL* 04/09/2000 - Use the latest Kamui macros
#define _KM_USE_VERTEX_MACRO_
#define _KM_USE_VERTEX_MACRO_L5_

#include <shinobi.h>
#include <kmbase.h>
#include <kamui2.h>
#include <kamuix.h>


// 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"

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

//#define MAPDRAW_SHOW_VIS_SPHERES

//#ifndef _WINDOWS
//#define DCACHE  0x1f800000
//#else
char pcDCacheData[1024*4];
#define DCACHE  pcDCacheData
//#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


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

extern int mapPolyCount;

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

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


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

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


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





// ===================================================
// 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)
{
}

#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)
{

}


#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;

#ifndef _WINDOWS
		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
		if(xy[v].x< -scrOffsetX)
			check|=OFFLEFT;
		if(xy[v].x> scrOffsetX)
			check|=OFFRIGHT;
		if(xy[v].y< -scrOffsetY)
			check|=OFFUP;
		if(xy[v].y> scrOffsetY)
			check|=OFFDOWN;
#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<<(4)))
			check|=OFFFRONT;

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

		if((checkstr->depths[v]) >= (MAX_MAP_DEPTH_CLIPCHECK<<4))
			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)
{
	int						i;
	FMA_MESH_HEADER			**mesh;
	WORLD_MESH_INSTANCE		*entry;

	// *ASL* 05/09/2000 - hold our dc mesh
	TDCWorldMesh			*dcWorldMesh;


	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
				{
					// set this meshes transform matrix
					MapDraw_SetMatrix(entry,shifts);
					// *ASL* 04/09/2000 - ..Then set mine
					{
						VECTOR v;
						v.vx = TransVector.vx;
						v.vy = TransVector.vy;
						v.vz = TransVector.vz + depthBump;			// add on depth bump
						SH4XD_SetMatrix(&RotMatrix, &v);
					}

					if (MapDraw_ClipCheck(entry,shifts))
					{
#ifdef _WINDOWS
						if (runningHard)
							PCH3D_MapDraw_DrawFMA_Mesh3(entry,shifts);
						else
							MapDraw_DrawFMA_Mesh3DC(entry,shifts);
#else
						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);
#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_Mesh3DC(instance,3);
#else
						MapDraw_DrawFMA_Mesh3DC(instance,3);
#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))
		{
			MapDraw_DrawSkyMeshDC((void *)*mesh);
		}
	}
}


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;
	}
	if(xy[0]+distancescale < 0 || xy[0] - distancescale > 640)	// tbd - check "resolution"
	{
//		printf("Radcheck %d %d %d Off H\n", xy[0],xy[1],distancescale);
		return 0;
	}
	if(xy[1]+distancescale < 0 || xy[1] - distancescale > 480)
	{
//		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;
}


// *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				*psxQuads;
	FMA_GT3				*psxTris;
	VERT				*psxVerts;
	KMDWORD				*vp;
	TDCVector4			*verts;
	TextureType			*map;
	int					i, j, *ip;
	float				tw,th;

	// 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++;
		}


		// ** 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, psxQuads = mesh->gt4s; j<mesh->n_gt4s; j++, psxQuads++)
		{
			// load this quads texture size
			tw = (float)DCKtextureList[psxQuads->tpage].w;
			th = (float)DCKtextureList[psxQuads->tpage].h;

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

			// polygon indices, strip header and texture map.. 1 32byte cache line
			map = &DCKtextureList[psxQuads->tpage];
			ip[3] = (int)map;

			// ** 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] = psxQuads->vert3 << 2;
			ip[5] = psxQuads->vert2 << 2;
			ip[6] = psxQuads->vert1 << 2;
			ip[7] = psxQuads->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, psxTris = mesh->gt3s; j < mesh->n_gt3s; j++, psxTris++)
		{
			// load this tris texture size
			tw = (float)DCKtextureList[psxTris->tpage].w;
			th = (float)DCKtextureList[psxTris->tpage].h;

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

			// polygon indices, strip header and texture map.. 1 32byte cache line
			map = &DCKtextureList[psxTris->tpage];
			ip[4] = (int)map;

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


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

		dcWorldMeshes[i].noofQuadPolys = 0;
		dcWorldMeshes[i].quadIndices = NULL;
		dcWorldMeshes[i].quadVerts = NULL;


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

		dcWorldMeshes[i].noofTriPolys = 0;
		dcWorldMeshes[i].triIndices = NULL;
		dcWorldMeshes[i].triVerts = NULL;
	}
}


/* ---------------------------------------------------------
   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 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[3];
		meshOffs[0] = instance->posx - mycamera.vpx;
		meshOffs[1] = instance->posy - mycamera.vpy;
		meshOffs[2] = instance->posz - mycamera.vpz;
		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?
			r0 = (int)&((TextureType *)r0)->stripHead;
			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?
			r0 = (int)&((TextureType *)r0)->stripHead;
			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;
		}
	}
	kmxxReleaseCurrentPtr(&vertexBufferDesc);
}
