/************************************************************************************
	PSX CORE (c) 1999 ISL

	psi.c:		Playstation Model (i) Handler
************************************************************************************/




// CUSTOM2 ("Direct2" in the test viewer program)
// This involves a rewrite to the PSI data format.
// ("BFF_PSI ... -version 1")

// Polys are grouped according to type, and are inserted into the
// global OT at their own individual depth.

// Lighting is of the "directiononly" type.
// I've only done the code for GT3 and GT4.
// Double sided polygons are converted to two singles in BFF_PSI

#include "global.h"

#include "custom.h"
#include "timer.h"

// The PSX makefile's auto-dependancy can't cope with ifdefs. Stick this into PSX_PC.H or something?
//#ifdef _WINDOWS
//#include "pcHardware3D.h"
//#endif

#ifndef _WINDOWS

#define DIRECT_SCALE_DEPTH_DOWN 5
//#define DIRECT_SCALE_DEPTH_DOWN 4

//#define DIRECT_DEPTH_OVCHECK
//#define DEPTH_AVERAGE

#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 GETN(n)( ((VERT *)( (int)(tfn) +(n<<1) )) )
//#define GETN2(n)( ((VERT *)( (int)(tfn) +(n<<1) )) )
#define GETN2(n)( ((VERT *)( (int)(tfn) +(n) )) )


// NEW prim structures with extra information at the end...

#endif





#ifndef _WINDOWS


/**************************************************************************

  "Direct Buffer Insertion" Lit plotter, for PSI-Version-1
  	
**************************************************************************/

void direct2PsiDrawInsertionPrimitives(PSIMODEL *model, long *tfv,long *tfd)
{
#ifndef DREAMCAST
	register PACKET*		packet = currentDisplayPage->primPtr;
#ifndef _WINDOWS
	register long clipflag asm("$20");
	register long deep asm("$19");
#else
	register long clipflag;
	register long deep;
#endif
	register D2M_TMD_P_GT4I	*opcd;
	unsigned long *ot = currentDisplayPage->ot;
	register long			t1;
	register long			t2;
	VERT 					*tfn = transformedNormals;

	int primsleft;
	DIRECT2MESH *d2m;

	clipflag = 0;

	d2m = (DIRECT2MESH *)(&model->pad[0]);


// ------ Gouraud Textured Tris -------
// Keep optimal, coz ginger and the dogs are "all gt3"
#define si ((POLY_GT3*)packet)
#define op ((D2M_TMD_P_GT3I*)opcd)

	op = d2m->gt3s;
	for(primsleft = d2m->n_gt3s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

#ifndef DEPTH_AVERAGE
		deep = (GETD(op->v2));
// to use the "_b", you need to fill out the PRECEDING two nops with non gte stuff
// And there should still be a bit of spare space afterwards
		gte_nclip_b();
#else
		gte_nclip();
#endif
		gte_stopz_cpu(clipflag);
			
		if ((clipflag < 0))
		{

			INCPOLYCOUNT(MODEL_POLYS);
			
#ifdef DEPTH_AVERAGE
			gte_ldsz3(GETD(op->v0),GETD(op->v1),GETD(op->v2));
   			gte_avsz3();
			gte_stotz_cpu(deep);
#endif


			t1 = *(u_long *) (&op->tu0);
			gte_stsxy3_gt3(si);	// 3 swc2's
			t2 = *(u_long *) (&op->tu1);
			gte_ldv3(GETN2(op->n0), GETN2(op->n1), GETN2(op->n2));

			*(u_long *)  (&si->u0) = t1;
			*(u_long *)  (&si->u1) = t2;

// For the nct_b, we *must* have a couple of instructions beforehand to
// occupy the buffering NOP slots between it and the ldv3
// We can stick a fair amount of code into the (30 cycle) calculation
// zone that's enforced before the strgb3
//				gte_nct();
			gte_nct_b();

			t1 = *(u_long *) (&op->tu2);
			addPrimLen((&ot[deep]),si,GT3_PRIMLEN,t2);
			*(u_long *)  (&si->u2) = t1;

			gte_strgb3(&si->r0, &si->r1, &si->r2);	// 3 swc2's
			si->code = GPU_COM_TG3;	// top bit of rgb0
			si++;
		}
	}
		
#undef op
#undef si

// ------ Gouraud Textured Quads -------
// (I've not checked the optimisation of this one so thoroughly - FW)

#define si ((POLY_GT4*)packet)
#define op opcd
	op = d2m->gt4s;
	for(primsleft = d2m->n_gt4s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

		gte_nclip();	// takes 8 cycles

#ifndef DEPTH_AVERAGE
		deep = (GETD(op->v2));
#endif
		gte_stopz_cpu(clipflag);

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

#ifdef DEPTH_AVERAGE
			gte_ldsz4(GETD(op->v0),GETD(op->v1),GETD(op->v2),GETD(op->v3));
   			gte_avsz4();
			gte_stotz_cpu(deep);
#endif


			addPrimLen((&ot[deep]),si,GT4_PRIMLEN,t1);

			t1 = *(u_long *) (&op->tu0);
			t2 = *(u_long *) (&op->tu1);
			*(u_long *)  (&si->u0) = t1;
			*(u_long *)  (&si->u1) = t2;
				
			gte_stsxy3_gt4(si);

			t1 = *(u_long *) (&op->tu2);
			t2 = *(u_long *) (&op->tu3);
			gte_ldv3(GETN2(op->n0), GETN2(op->n1), GETN2(op->n2));
			*(u_long *)  (&si->u2) = t1;
			*(u_long *)  (&si->u3) = t2;
									
			t1 = *(u_long *) (&GETV(op->v3));
//					gte_nct();
			gte_nct_b();			
			gte_strgb3(&si->r0, &si->r1, &si->r2);
			*(u_long *)  (&si->x3) = t1;

			gte_ldv0(GETN2(op->n3));
			gte_ncs();			// NormalColorCol

			gte_strgb(&si->r3);
			si->code = GPU_COM_TG4;	//op->cd;
			si++;
		}
	}
#undef op
#undef si
//	sortCount = localSortCount;
	currentDisplayPage->primPtr = packet;

#endif
}



void direct2PsiDrawInsertionPrimitivesDepthCheck(PSIMODEL *model, long *tfv,long *tfd)
{
#ifndef DREAMCAST
	register PACKET*		packet = currentDisplayPage->primPtr;
#ifndef _WINDOWS
	register long clipflag asm("$20");
	register long deep asm("$19");
#else
	register long clipflag;
	register long deep;
#endif
	register D2M_TMD_P_GT4I	*opcd;
	unsigned long *ot = currentDisplayPage->ot;
	register long			t1;
	register long			t2;
	VERT 					*tfn = transformedNormals;

	int primsleft;
	DIRECT2MESH *d2m;

	clipflag = 0;

	d2m = (DIRECT2MESH *)(&model->pad[0]);


// ------ Gouraud Textured Tris -------
// Keep optimal, coz ginger and the dogs are "all gt3"
#define si ((POLY_GT3*)packet)
#define op ((D2M_TMD_P_GT3I*)opcd)

	op = d2m->gt3s;
	for(primsleft = d2m->n_gt3s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

//		gte_nclip();	// takes 8 cycles

#ifndef DEPTH_AVERAGE
		deep = (GETD(op->v2));
// to use the "_b", you need to fill out the PRECEDING two nops with non gte stuff
// And there should still be a bit of spare space afterwards
		gte_nclip_b();
		if(deep < 0) deep = 0;
		if(deep > 1023) deep = 1023;
#else
		gte_nclip();
#endif

		gte_stopz_cpu(clipflag);
			
		if ((clipflag < 0))
		{
			INCPOLYCOUNT(MODEL_POLYS);

			
#ifdef DEPTH_AVERAGE
			gte_ldsz3(GETD(op->v0),GETD(op->v1),GETD(op->v2));
   			gte_avsz3();
			gte_stotz_cpu(deep);
			if(deep < 0) deep = 0;
			if(deep > 1023) deep = 1023;
#endif


			t1 = *(u_long *) (&op->tu0);
			gte_stsxy3_gt3(si);	// 3 swc2's
			t2 = *(u_long *) (&op->tu1);
			gte_ldv3(GETN2(op->n0), GETN2(op->n1), GETN2(op->n2));

			*(u_long *)  (&si->u0) = t1;
			*(u_long *)  (&si->u1) = t2;

// For the nct_b, we *must* have a couple of instructions beforehand to
// occupy the buffering NOP slots between it and the ldv3
// We can stick a fair amount of code into the (30 cycle) calculation
// zone that's enforced before the strgb3
//				gte_nct();
			gte_nct_b();

			t1 = *(u_long *) (&op->tu2);
			addPrimLen((&ot[deep]),si,GT3_PRIMLEN,t2);
			*(u_long *)  (&si->u2) = t1;

			gte_strgb3(&si->r0, &si->r1, &si->r2);	// 3 swc2's
			si->code = GPU_COM_TG3;	// top bit of rgb0
			si++;
		}
	}
		
#undef op
#undef si

// ------ Gouraud Textured Quads -------
// (I've not checked the optimisation of this one so thoroughly - FW)

#define si ((POLY_GT4*)packet)
#define op opcd
	op = d2m->gt4s;
	for(primsleft = d2m->n_gt4s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

		gte_nclip();	// takes 8 cycles

#ifndef DEPTH_AVERAGE
		deep = (GETD(op->v2));
		if(deep < 0) deep = 0;
		if(deep > 1023) deep = 1023;
#endif
		gte_stopz_cpu(clipflag);

				
		if ((clipflag < 0))
		{

			INCPOLYCOUNT(MODEL_POLYS);
#ifdef DEPTH_AVERAGE
			gte_ldsz4(GETD(op->v0),GETD(op->v1),GETD(op->v2),GETD(op->v3));
   			gte_avsz4();
			gte_stotz_cpu(deep);
			if(deep < 0) deep = 0;
			if(deep > 1023) deep = 1023;
#endif


			addPrimLen((&ot[deep]),si,GT4_PRIMLEN,t1);

			t1 = *(u_long *) (&op->tu0);
			t2 = *(u_long *) (&op->tu1);
			*(u_long *)  (&si->u0) = t1;
			*(u_long *)  (&si->u1) = t2;
				
			gte_stsxy3_gt4(si);

			t1 = *(u_long *) (&op->tu2);
			t2 = *(u_long *) (&op->tu3);
			gte_ldv3(GETN2(op->n0), GETN2(op->n1), GETN2(op->n2));
			*(u_long *)  (&si->u2) = t1;
			*(u_long *)  (&si->u3) = t2;
									
			t1 = *(u_long *) (&GETV(op->v3));
//					gte_nct();
			gte_nct_b();			
			gte_strgb3(&si->r0, &si->r1, &si->r2);
			*(u_long *)  (&si->x3) = t1;

			gte_ldv0(GETN2(op->n3));
			gte_ncs();			// NormalColorCol

			gte_strgb(&si->r3);
			si->code = GPU_COM_TG4;	//op->cd;
			si++;
		}
	}
#undef op
#undef si
//	sortCount = localSortCount;
	currentDisplayPage->primPtr = packet;

#endif
}


// We also want a "use the object's gouraud values, but no lighting" plotter.

void direct2PsiDrawInsertionPrimitivesPrelitDepthCheck(PSIMODEL *model, long *tfv,long *tfd)
{
#ifndef DREAMCAST
	register PACKET*		packet = currentDisplayPage->primPtr;
#ifndef _WINDOWS
	register long clipflag asm("$20");
	register long deep asm("$19");
#else 
	register long clipflag;
	register long deep;
#endif
	register D2M_TMD_P_GT4I	*opcd;
	unsigned long *ot = currentDisplayPage->ot;
	register long			t1;
	register long			t2;
	VERT 					*tfn = transformedNormals;

	int primsleft;
	DIRECT2MESH *d2m;

	clipflag = 0;

	d2m = (DIRECT2MESH *)(&model->pad[0]);


// ------ Gouraud Textured Tris -------
// Keep optimal, coz ginger and the dogs are "all gt3"
#define si ((POLY_GT3*)packet)
#define op ((D2M_TMD_P_GT3I*)opcd)

	op = d2m->gt3s;
	for(primsleft = d2m->n_gt3s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

//		gte_nclip();	// takes 8 cycles

#ifndef DEPTH_AVERAGE
		deep = (GETD(op->v2));
// to use the "_b", you need to fill out the PRECEDING two nops with non gte stuff
// And there should still be a bit of spare space afterwards
		gte_nclip_b();
		if(deep < 0) deep = 0;
		if(deep > 1023) deep = 1023;
#else
		gte_nclip();
#endif

		gte_stopz_cpu(clipflag);
			
		if ((clipflag < 0))
		{

			INCPOLYCOUNT(MODEL_POLYS);
			
#ifdef DEPTH_AVERAGE
			gte_ldsz3(GETD(op->v0),GETD(op->v1),GETD(op->v2));
   			gte_avsz3();
			gte_stotz_cpu(deep);
			if(deep < 0) deep = 0;
			if(deep > 1023) deep = 1023;
#endif


			t1 = *(u_long *) (&op->tu0);
			gte_stsxy3_gt3(si);	// 3 swc2's
			t2 = *(u_long *) (&op->tu1);
			*(u_long *)  (&si->u0) = t1;
			*(u_long *)  (&si->u1) = t2;

			t1 = *(u_long *) (&op->r0);
			t2 = *(u_long *) (&op->r1);
			*(u_long *)  (&si->r0) = t1;
			*(u_long *)  (&si->r1) = t2;

			t1 = *(u_long *) (&op->tu2);
			t2 = *(u_long *) (&op->r2);
			*(u_long *)  (&si->u2) = t1;
			*(u_long *)  (&si->r2) = t2;
			addPrimLen((&ot[deep]),si,GT3_PRIMLEN,t2);

			si++;
		}
	}
		
#undef op
#undef si

// ------ Gouraud Textured Quads -------
// (I've not checked the optimisation of this one so thoroughly - FW)

#define si ((POLY_GT4*)packet)
#define op opcd
	op = d2m->gt4s;
	for(primsleft = d2m->n_gt4s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

		gte_nclip();	// takes 8 cycles

#ifndef DEPTH_AVERAGE
		deep = (GETD(op->v2));
		if(deep < 0) deep = 0;
		if(deep > 1023) deep = 1023;
#endif
		gte_stopz_cpu(clipflag);

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

#ifdef DEPTH_AVERAGE
			gte_ldsz4(GETD(op->v0),GETD(op->v1),GETD(op->v2),GETD(op->v3));
   			gte_avsz4();
			gte_stotz_cpu(deep);
			if(deep < 0) deep = 0;
			if(deep > 1023) deep = 1023;
#endif


			addPrimLen((&ot[deep]),si,GT4_PRIMLEN,t1);

			t1 = *(u_long *) (&op->tu0);
			t2 = *(u_long *) (&op->tu1);
			*(u_long *)  (&si->u0) = t1;
			*(u_long *)  (&si->u1) = t2;

			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;

			gte_stsxy3_gt4(si);

			t1 = *(u_long *) (&op->tu2);
			t2 = *(u_long *) (&op->tu3);
			*(u_long *)  (&si->u2) = t1;
			*(u_long *)  (&si->u3) = t2;
									
			t1 = *(u_long *) (&GETV(op->v3));
			*(u_long *)  (&si->x3) = t1;

			si++;
		}
	}
#undef op
#undef si
//	sortCount = localSortCount;
	currentDisplayPage->primPtr = packet;

#endif
}




/**************************************************************************

  "SortedIndex" Lit plotter, for PSI-Version-1
  (Used during the death fade-out, for instance. Whenever there's a depth override, in essence)
  	
**************************************************************************/

// Pre-sorting the prims of a direct2 mesh should be done
// 1) If depthoverride's set (ie, we're not planning to do the full poly-insersion sort)
// 2) The model could overlaps the start/end of the OT (in which case the lack of overflow checking in the insertion version could cause probs)

// If this rou's running out of registers, it may be worth
// allocating ourselves some scratchpad space
void direct2PsiSortPrimitives(PSIMODEL *model, long *tfv, long *tfd)
{
#ifndef DREAMCAST

#ifndef _WINDOWS
	register long clipflag asm("$20");
#else
	register long clipflag;
#endif
	register D2M_TMD_P_GT4I	*opcd;
	int				*sl;
	int localSortCount;

	long deep;
	int primsleft;
	int localMinDepth = minDepth;

	DIRECT2MESH *d2m;

	clipflag = 0;

	d2m = (DIRECT2MESH *)(&model->pad[0]);

	(TMD_P_GT3I*)sl = &sortedIndex[0];

	localSortCount = sortCount;

//	utilPrintf("n_3s, 4s = %d %d\n",d2m->n_gt3s,d2m->n_gt4s);

#define op opcd
	op = d2m->gt4s;
	for(primsleft = d2m->n_gt4s; primsleft != 0; primsleft--, op++)
	{
		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices
		gte_nclip();	// takes 8 cycles
		deep = (GETD(op->v2) - localMinDepth);	// fill in that 8-cycle gap
		gte_stopz_cpu(clipflag);
		if (clipflag < 0)
		{
			op->next = sl[deep];
			sl[deep] = (int)op;
			localSortCount ++;
		}
	}
#undef op
#define op ((D2M_TMD_P_GT3I*)opcd)
	op = d2m->gt3s;

//	utilPrintf("n_3s, 4s = %d %d\n",d2m->n_gt3s,d2m->n_gt4s);

	for(primsleft = d2m->n_gt3s; primsleft != 0; primsleft--, op++)
	{
//	utilPrintf(".\n");

		gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices
		gte_nclip();	// takes 8 cycles
		deep = (GETD(op->v2) - localMinDepth);
		gte_stopz_cpu(clipflag);
		if (clipflag < 0)
		{
			op->next = sl[deep];
			sl[deep] = (int)op;
			localSortCount ++;
		}
	}
#undef op
	sortCount = localSortCount;
//	utilPrintf("sortc = %d\n",sortCount);
#endif
}


void direct2PsiDrawSortedPrimitives(int depth,long *tfv)
{
#ifndef DREAMCAST
	register PACKET*		packet = currentDisplayPage->primPtr;
	VERT 					*tfn = transformedNormals;
	register D2M_TMD_P_GT4I		*opcd;
	int						primsleft;
	ULONG					*sorts = sortedIndex;
	register long			t1;
	register long			t2;
	unsigned long *ot = currentDisplayPage->ot+depth;
	int						gteH;

	gte_ReadGeomScreen(&gteH);

	opcd = 0;
 
	for(primsleft = sortCount; primsleft; primsleft--)
	{

		while(opcd==0)
		{
			(int)opcd = *(sorts++);
		}

		switch (opcd->cd & (0xff-2))
		{

#define si ((POLY_GT3*)packet)
#define op ((D2M_TMD_P_GT3I*)opcd)
			case GPU_COM_TG3:
				INCPOLYCOUNT(MODEL_POLYS);
				gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices
				
				t1 = *(u_long *) (&op->tu0);
				gte_stsxy3_gt3(si);	// 3 swc2's
				t2 = *(u_long *) (&op->tu1);
				gte_ldv3(GETN(op->v0), GETN(op->v1), GETN(op->v2));

				*(u_long *)  (&si->u0) = t1;
				*(u_long *)  (&si->u1) = t2;


// For the nct_b, we *must* have a couple of instructions beforehand to
// occupy the buffering NOP slots between it and the ldv3
// We can stick a fair amount of code into the (30 cycle) calculation
// zone that's enforced before the strgb3
//				gte_nct();
				gte_nct_b();


				addPrimLen(ot,si,GT3_PRIMLEN,t2);
				t1 = *(u_long *) (&op->tu2);
				op = op->next;

				*(u_long *)  (&si->u2) = t1;

				gte_strgb3(&si->r0, &si->r1, &si->r2);	// 3 swc2's
				si->code = GPU_COM_TG3;	// top bit of rgb0
				si++;
				break;
#undef si
#undef op

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

			case GPU_COM_TG4:
				INCPOLYCOUNT(MODEL_POLYS);
				addPrimLen(ot,si,GT4_PRIMLEN,t1);

				gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

				t1 = *(u_long *) (&op->tu0);
				t2 = *(u_long *) (&op->tu1);
				*(u_long *)  (&si->u0) = t1;
				*(u_long *)  (&si->u1) = t2;
					
				gte_stsxy3_gt4(si);

				t1 = *(u_long *) (&op->tu2);
				t2 = *(u_long *) (&op->tu3);
				*(u_long *)  (&si->u2) = t1;
				*(u_long *)  (&si->u3) = t2;
										
				*(u_long *)  (&si->x3) = *(u_long *) (&GETV(op->v3));
				gte_ldv3(GETN(op->v0), GETN(op->v1), GETN(op->v2));
				gte_nct();			
				gte_strgb3(&si->r0, &si->r1, &si->r2);

				gte_ldv0(GETN(op->v3));
				gte_ncs();			// NormalColorCol
				(int)op = op->next;

				gte_strgb(&si->r3);
				si->code = GPU_COM_TG4;	//op->cd;
				si++;
				break;
#undef si
#undef op
		default:
			printf("prim %d\n",opcd->cd);
			break;
		}
	}
	currentDisplayPage->primPtr = packet;

#endif
}



void direct2PsiDrawSortedPrimitivesPrelit(int depth,long *tfv)
{
#ifndef DREAMCAST
	register PACKET*		packet = currentDisplayPage->primPtr;
	VERT 					*tfn = transformedNormals;
	register D2M_TMD_P_GT4I		*opcd;
	int						primsleft;
	ULONG					*sorts = sortedIndex;
	register long			t1;
	register long			t2;
	unsigned long *ot = currentDisplayPage->ot+depth;
	int						gteH;

	gte_ReadGeomScreen(&gteH);

	opcd = 0;
 
	for(primsleft = sortCount; primsleft; primsleft--)
	{

		while(opcd==0)
		{
			(int)opcd = *(sorts++);
		}

		switch (opcd->cd & (0xff-2))
		{

#define si ((POLY_GT3*)packet)
#define op ((D2M_TMD_P_GT3I*)opcd)
			case GPU_COM_TG3:
				INCPOLYCOUNT(MODEL_POLYS);
				gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices
				
				t1 = *(u_long *) (&op->tu0);
				gte_stsxy3_gt3(si);	// 3 swc2's
				t2 = *(u_long *) (&op->tu1);

				*(u_long *)  (&si->u0) = t1;
				*(u_long *)  (&si->u1) = t2;

				t1 = *(u_long *) (&op->r0);
				t2 = *(u_long *) (&op->r1);
				*(u_long *)  (&si->r0) = t1;
				*(u_long *)  (&si->r1) = t2;

				addPrimLen(ot,si,GT3_PRIMLEN,t2);
				t1 = *(u_long *) (&op->tu2);
				t2 = *(u_long *) (&op->r2);
				op = op->next;

				*(u_long *)  (&si->u2) = t1;
				*(u_long *)  (&si->r2) = t2;

				si->code = GPU_COM_TG3;	// top bit of rgb0
				si++;
				break;
#undef si
#undef op

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

			case GPU_COM_TG4:
				INCPOLYCOUNT(MODEL_POLYS);
				addPrimLen(ot,si,GT4_PRIMLEN,t1);

				gte_ldsxy3(GETV(op->v0), GETV(op->v1), GETV(op->v2));		// Load 1st three vertices

				t1 = *(u_long *) (&op->tu0);
				t2 = *(u_long *) (&op->tu1);
				*(u_long *)  (&si->u0) = t1;
				*(u_long *)  (&si->u1) = t2;
					
				gte_stsxy3_gt4(si);

				t1 = *(u_long *) (&op->tu2);
				t2 = *(u_long *) (&op->tu3);
				*(u_long *)  (&si->u2) = t1;
				*(u_long *)  (&si->u3) = t2;
										
				*(u_long *)  (&si->x3) = *(u_long *) (&GETV(op->v3));


				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;

				(int)op = op->next;

				si->code = GPU_COM_TG4;	//op->cd;
				si++;
				break;
#undef si
#undef op
		default:
			printf("prim %d\n",opcd->cd);
			break;
		}
	}
	currentDisplayPage->primPtr = packet;

#endif
}



/**************************************************************************
	FUNCTION:	PSIDrawSegments
	PURPOSE:	draw all object segments of an actor
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

// NB - This function uses the entire D-Cache for vertex transformation if the object fits...)
//void customPsiDrawSegments(PSIDATA *psiData)
void direct2psiDrawSegments(PSIDATA *psiData)
{
	long		*tfvbase;
	long		*tfdbase;
	register long		*tfv;
	register long		*tfd;
	register PSIOBJECT	*world;
	PSIMODELCTRL		*modctrl = &PSImodelctrl;
//	LONG				depth;
	LONG				s = modctrl->sorttable;
	int					loop, obs, i,store;
	VERT				*tfn;

//	TIMER_START(TIMER_PSIREND);	// Our version of Timer can cope with restarting timers
	
	world = psiData->object;
	obs = psiData->numObjects;

	// transform all the vertices (by heirarchy)
	// tfv == one long list of transformed vertices


	tfn = transformedNormals - 1;



	tfTotal = 0;
	for (loop = 0; loop < obs; loop++)
	{
		world = (PSIOBJECT*)psiData->objectTable[loop];
		tfTotal += world->meshdata->vern;
	}

	if(tfTotal <= 126)
	{
		tfvbase = (void *)DCACHE;
		tfdbase = (void *)(DCACHE + 512);
	}
	else if(tfTotal <= 255)
	{
		// nb - not 256 - "transformvertexlist" assumes a multiple of 3.
		tfvbase = (void *)DCACHE;
		tfdbase = transformedDepths;
	}
	else
	{
		tfvbase = transformedVertices;
		tfdbase = transformedDepths;
	}


	tfv = tfvbase;
	tfd = tfdbase;

	tfTotal = 0;

	for (loop = 0; loop < obs; loop++)
	{
		world = (PSIOBJECT*)psiData->objectTable[loop];

	 	gte_SetRotMatrix(&world->matrixscale);			 
	   	gte_SetTransMatrix(&world->matrixscale);	

//	Removals by RFW. See below inside the ifdefs
//		gte_ldv0(&world->meshdata->center);
//		gte_rtps();
		modctrl->NormTop = world->meshdata->nortop;
//		gte_stszotz(&world->depth);


#ifdef DREAMCAST
	// RFW, fixing a big OOPS rather late in the day. 10 Oct 00, in fact.
		transformVertexListA((VERT *)&world->meshdata->center, 1, tfv, tfd );
		world->depth = tfd[0];

		transformVertexListA(world->meshdata->vertop, world->meshdata->vern, tfv, tfd );
#else
	// RFW, fixing a big OOPS rather late in the day. 10 Oct 00, in fact.
	// Note that the above "world depth" doesn't actually get the world depth at all.
	// center is an SVECTOR, txverts is a VERT. Both start with 3 shorts
		transformVertexListA_ChickenRun((VERT *)&world->meshdata->center, 1, tfv, tfd );
		world->depth = tfd[0];

		transformVertexListA_ChickenRun(world->meshdata->vertop, world->meshdata->vern, tfv, tfd );
#endif

// 3/10ths of vert stuff is in the normal transforms.
		if (modctrl->lighting)
		{
			register SVECTOR	*np;
			gte_SetRotMatrix(&world->matrix);			 
			np = (SVECTOR*)world->meshdata->nortop;
	   		//gte_SetTransMatrix(&world->matrix);	

			for(i = world->meshdata->norn; i != 0; i--)
			{
				gte_ldv0(np);
				gte_rtv0();		// an 8 cycle job, with two nops preceding it
				np++;			// do stuff while the rt's going on
				tfn++;
				gte_stsv((int)(tfn));	//GETN(0));
			}
		}

		tfv += world->meshdata->vern;
		tfd += world->meshdata->vern;
		tfTotal += world->meshdata->vern;
	}


#ifdef DREAMCAST
	for(i = 0; i < tfTotal; i++)
	{
		if((globalDeathOn == 0) && (modctrl->depthoverride))
	 		tfdbase[i] = modctrl->depthoverride;//>> DIRECT_SCALE_DEPTH_DOWN;

		tfdbase[i]>>=DCscaledown;

		if(modctrl->depthoverride == 0)
			tfdbase[i]+=depthBump;
	}
#else
#ifdef DIRECT_SCALE_DEPTH_DOWN
	{
		for(i = 0; i < tfTotal; i++)
			tfdbase[i] = tfdbase[i] >> DIRECT_SCALE_DEPTH_DOWN;
	}
#endif
#endif


	modctrl->PrimTop = (ULONG*)psiData->primitiveList;

	if (psiData->flags & ACTOR_DYNAMICSORT)
	{
		if(modctrl->lighting == NOLIGHTING)
		{
#ifdef DREAMCAST
			PSIMODEL *model;
			model = (PSIMODEL *)psiData->modelName;
			direct2PsiDrawInsertionPrimitivesPrelitDepthCheckDC(model, tfvbase,tfdbase);
#else
	 		if(PSImodelctrl.depthoverride)
			{
	 			customPsiCalcMaxMin(psiData, tfdbase);

				world = (PSIOBJECT*)psiData->objectTable[0];

				modctrl->PrimLeft = psiData->noofPrims;
				modctrl->VertTop = world->meshdata->vertop;

				direct2PsiSortPrimitives((PSIMODEL *)psiData->modelName, tfvbase,tfdbase);
				modctrl->polysdrawn = sortCount;

	// It's assumed you've set the depth override to between 0 & 1023 :)
				direct2PsiDrawSortedPrimitivesPrelit( (PSImodelctrl.depthoverride) >> modctrl->depthShift, tfvbase);
			}
			else
			{
				PSIMODEL *model;
				model = (PSIMODEL *)psiData->modelName;
				direct2PsiDrawInsertionPrimitivesPrelitDepthCheck(model, tfvbase,tfdbase);
			}
#endif
		}
		else if(PSImodelctrl.depthoverride)
		{
#ifndef DREAMCAST
	 		customPsiCalcMaxMin(psiData, tfdbase);
#endif
			world = (PSIOBJECT*)psiData->objectTable[0];

			modctrl->PrimLeft = psiData->noofPrims;
			modctrl->VertTop = world->meshdata->vertop;

#ifdef DREAMCAST
			store = depthBump;
			depthBump = 0;
			direct2PsiDrawFlattenedPrimitivesDC((PSIMODEL *)psiData->modelName, tfvbase,tfdbase);
			depthBump = store;
#else
			direct2PsiSortPrimitives((PSIMODEL *)psiData->modelName, tfvbase,tfdbase);
			modctrl->polysdrawn = sortCount;

// It's assumed you've set the depth override to between 0 & 1023 :)
			direct2PsiDrawSortedPrimitives( (PSImodelctrl.depthoverride) >> modctrl->depthShift, tfvbase);
#endif
		}
		else
		{
			PSIMODEL *model;

			model = (PSIMODEL *)psiData->modelName;
			world = (PSIOBJECT*)psiData->objectTable[0];

// See if we can use the fast plotter which doesn't range-check the depth values
// This test is now essential (see the fireworks subgame, in particular)
#ifdef DREAMCAST
			direct2PsiDrawInsertionPrimitivesDC(model, tfvbase,tfdbase);
#else	
			if(world->depth - model->radius < 8 || world->depth + model->radius > (1020 <<DIRECT_SCALE_DEPTH_DOWN))
			{
				direct2PsiDrawInsertionPrimitivesDepthCheck(model, tfvbase,tfdbase);
			}
			else
			{
				direct2PsiDrawInsertionPrimitives(model, tfvbase,tfdbase);
			}
#endif
		}

	}
	else
	{
// speed optim version is not un-sorted-object compatable
	}
//	TIMER_STOP(TIMER_PSIREND);
}



#endif


void direct2PsiFixupPrims(PSIMODEL *psiModel)
{
	TextureType *sprt;
	int p,a;
	char *primitive;
	UBYTE s;

	//int ix; //temp

	DIRECT2MESH *d2m;

	d2m = (DIRECT2MESH *)(&psiModel->pad[0]);

// Gouraud Textured Tris
	for(p = 0; p < d2m->n_gt3s; p++)
	{
		sprt = PageTexture( d2m->gt3s[p].clut ); // clut= texture index number
		
		d2m->gt3s[p].code = GPU_COM_TG3;
		if (d2m->gt3s[p].tpage) 
		{
			d2m->gt3s[p].cd |= 2;
			d2m->gt3s[p].code |= 2;
		}
#ifdef _WINDOWS
		if (sprt)
		{
#endif
		d2m->gt3s[p].tpage |= sprt->tpage;
		d2m->gt3s[p].clut = sprt->clut;
#ifdef _WINDOWS
		};
#endif

		d2m->gt3s[p].in=(sizeof(D2M_TMD_P_GT3I)/4)-1;	//ilen
		d2m->gt3s[p].out=0x9; 						//olen   

#ifdef _WINDOWS
		if (sprt)
		{
#endif
		d2m->gt3s[p].tu0 += sprt->x;
		d2m->gt3s[p].tu1 += sprt->x;
		d2m->gt3s[p].tu2 += sprt->x;

		d2m->gt3s[p].tv0 += sprt->y;
		d2m->gt3s[p].tv1 += sprt->y;
		d2m->gt3s[p].tv2 += sprt->y;
#ifdef _WINDOWS
		};
#endif

#ifndef _WINDOWS
		d2m->gt3s[p].v0 *= 4;	// Hmmm. This lot should be at the PC end
		d2m->gt3s[p].v1 *= 4;
		d2m->gt3s[p].v2 *= 4;

		d2m->gt3s[p].n0 = d2m->gt3s[p].v0 * 2;
		d2m->gt3s[p].n1 = d2m->gt3s[p].v1 * 2;
		d2m->gt3s[p].n2 = d2m->gt3s[p].v2 * 2;
#endif


	}

// Gouraud Textured Quads
	for(p = 0; p < d2m->n_gt4s; p++)
	{

		sprt = PageTexture( d2m->gt4s[p].clut ); // clut= texture index number
		d2m->gt4s[p].code = GPU_COM_TG4;

		if (d2m->gt4s[p].tpage) 
		{
			d2m->gt4s[p].cd |= 2;
			d2m->gt4s[p].code |= 2;
		}

#ifdef _WINDOWS
		if (sprt)
		{
#endif
		d2m->gt4s[p].tpage |= sprt->tpage;
		d2m->gt4s[p].clut = sprt->clut;
#ifdef _WINDOWS
		};
#endif

		d2m->gt4s[p].in=(sizeof(D2M_TMD_P_GT4I)/4)-1;	//ilen
		d2m->gt4s[p].out=0xc; 						//olen   

#ifdef _WINDOWS
		if (sprt)
		{
#endif
		d2m->gt4s[p].tu0 += sprt->x;
		d2m->gt4s[p].tu1 += sprt->x;
		d2m->gt4s[p].tu2 += sprt->x;
		d2m->gt4s[p].tu3 += sprt->x;

		d2m->gt4s[p].tv0 += sprt->y;
		d2m->gt4s[p].tv1 += sprt->y;
		d2m->gt4s[p].tv2 += sprt->y;
		d2m->gt4s[p].tv3 += sprt->y;
#ifdef _WINDOWS
		};
#endif

#ifndef _WINDOWS
		d2m->gt4s[p].v0 *= 4;
		d2m->gt4s[p].v1 *= 4;
		d2m->gt4s[p].v2 *= 4;
		d2m->gt4s[p].v3 *= 4;

		d2m->gt4s[p].n0 = d2m->gt4s[p].v0 * 2;
		d2m->gt4s[p].n1 = d2m->gt4s[p].v1 * 2;
		d2m->gt4s[p].n2 = d2m->gt4s[p].v2 * 2;
		d2m->gt4s[p].n3 = d2m->gt4s[p].v3 * 2;
#endif

	}					
}


PSIMODEL *direct2psiFixup(char *addr)
{
	PSIMESH *mesh;
	int i;
	PSIMODEL *psiModel;

	psiModel = (PSIMODEL*)addr;

	currentPSI = psiModel;

	// do check for "PSI",0
//	if(strcmp((char*)psiModel,"PSI")!=0){ utilPrintf("%s is not a valid PSI file.\n",addr); return 0; }

	// primitive offset pointer
	psiModel->primOffset += (int)addr;
	
	// list of texture names
	psiModel->textureOffset += (int)addr;
	
	// mesh offset
	psiModel->firstMesh += (int)addr;
	
	//	offset to anim frame list
	psiModel->animSegmentList += (int)addr;

	// malloc transformation vertex,normal and depth buffers to biggest
	// model vert count loaded so far
	i = psiModel->noofVerts;
	if ( i>biggestVertexModel )
	{
		biggestVertexModel = i;
	}

//	DisplayPSI(psiModel);

	// number of textures
#ifdef DREAMCAST
	(char*)currentTextureList = (PSITEXTURES*)psiModel->textureOffset;
#else
	(char*)currentTextureList = psiModel->textureOffset;
#endif
//	numoftextures = psiModel->noofTextures;
//	for (i=0; i<numoftextures; i++) utilPrintf("%d: %s\n",i,&textureList[i]);

	
	
	// PSI MESH LIST

	mesh = (PSIMESH*)psiModel->firstMesh;

	psiFixupMesh(mesh);
	psiFixupOneRoot(mesh);

//	psiFixupPrims(psiModel);
#ifdef DREAMCAST
	direct2PsiFixupPrimsDC(psiModel);
#else
	direct2PsiFixupPrims(psiModel);
#endif
//	utilPrintf("Load PSI done\n");

	// add to loaded model list (for checking re-loads)
//	psiModelList[psiModelListLen] = psiModel;
//	strncpy( psiModelListNames[psiModelListLen], PSIname, 16);
//	psiModelListCRC[psiModelListLen] = psiCRCName( PSIname );
//	psiModelListLen++;


// A quick little fix so we have no differences in bff_load...
// Moved up a level
//	customPsiFixupPrims(psiModel);



	return psiModel;
}


void BFF_Link_PSI1(PSIMODEL *psiModel)
{
	DIRECT2MESH *d2m;

	d2m = (DIRECT2MESH *)(&psiModel->pad[0]);
	d2m->fg3s = ADD2POINTER(d2m->fg3s,(int)psiModel);
	d2m->fg4s = ADD2POINTER(d2m->fg4s,(int)psiModel);
	d2m->ft3s = ADD2POINTER(d2m->ft3s,(int)psiModel);
	d2m->ft4s = ADD2POINTER(d2m->ft4s,(int)psiModel);
	d2m->gt3s = ADD2POINTER(d2m->gt3s,(int)psiModel);
	d2m->gt4s = ADD2POINTER(d2m->gt4s,(int)psiModel);
	d2m->sprs = ADD2POINTER(d2m->sprs,(int)psiModel);

#ifdef _WINDOWS

	d2mPCH3D_CreateList(psiModel);

#endif

	direct2psiFixup((char *)psiModel);
//	direct2PsiFixupPrims((PSIMODEL *)psiModel);


//	utilPrintf("d2m->n_gt4s = %d\n",d2m->n_gt4s);

//	d2m->n_gt4s = 0;

//	psiModelListLen--;
}


short instance_tutv(char *tutv, TextureType *p1, TextureType *p2)
{
	char tu,tv;
	tu = tutv[0] - p1->x + p2->x;
	tv = tutv[1] - p1->y + p2->y;
	return (((short)tu) | ((((short)tv) << 8)));
}



void direct2FindTextureInstances(PSIMODEL *model,TEXINSTANCE *instance,TextureType *src)
{
	DIRECT2MESH *d2m;
	D2M_TMD_P_GT4I *gt4;
	D2M_TMD_P_GT3I *gt3;
	int i;

	d2m = (DIRECT2MESH *)(&model->pad[0]);

	gt3 = d2m->gt3s;

// tpage matching ain't enough. It's tpage, and tu/tv in a valid area
//	utilPrintf("ref = %4x\n",(int)src->tpage);
	for(i = 0; i < d2m->n_gt3s; i++,gt3++)
	{
//		utilPrintf("=%4x\n",(int)gt3->tpage);
#ifndef _WINDOWS
		if((gt3->tpage & ~0x60) == src->tpage
			&& gt3->tu0 >= src->u0 && gt3->tu0 <= src->u1
			&& gt3->tv0 >= src->v0 && gt3->tv0 <= src->v2
			)
#else 
		if((gt3->clut == src->clut)
			&& gt3->tu0 >= src->u0 && gt3->tu0 <= src->u1
			&& gt3->tv0 >= src->v0 && gt3->tv0 <= src->v2
			)
#endif
		{
			instance->pointers[instance->n_pointers] = &gt3->tpage;
			instance->values[instance->n_pointers] =
				(gt3->tpage & 0x60) | instance->tex.tpage;
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt3->tu0;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt3->tu0,src,&instance->tex);
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt3->tu1;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt3->tu1,src,&instance->tex);
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt3->tu2;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt3->tu2,src,&instance->tex);
			instance->n_pointers++;
		}
	}
	gt4 = d2m->gt4s;
	for(i = 0; i < d2m->n_gt4s; i++,gt4++)
	{
#ifndef _WINDOWS
		if((gt4->tpage & 0x60) == src->tpage
			&& gt4->tu0 >= src->u0 && gt4->tu0 <= src->u1
			&& gt4->tv0 >= src->v0 && gt4->tv0 <= src->v2

			)
#else
		if((gt4->clut == src->clut)
			&& gt4->tu0 >= src->u0 && gt4->tu0 <= src->u1
			&& gt4->tv0 >= src->v0 && gt4->tv0 <= src->v2

			)
#endif
		{
			instance->pointers[instance->n_pointers] = &gt4->tpage;
			instance->values[instance->n_pointers] =
				(gt4->tpage & 0x60) | instance->tex.tpage;
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt4->tu0;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt4->tu0,src,&instance->tex);
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt4->tu1;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt4->tu1,src,&instance->tex);
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt4->tu2;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt4->tu2,src,&instance->tex);
			instance->n_pointers++;

			instance->pointers[instance->n_pointers] = (short *)&gt4->tu3;
			instance->values[instance->n_pointers] = instance_tutv(
				&gt4->tu3,src,&instance->tex);
			instance->n_pointers++;
		}
	}
	ASSERT(instance->n_pointers < MAX_INSTANCE_POINTERS);
}
