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



void direct2PsiDrawInsertionPrimitivesDepthCheck(PSIMODEL *model, long *tfv,long *tfd)
{
}


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

void direct2PsiDrawInsertionPrimitivesPrelitDepthCheck(PSIMODEL *model, long *tfv,long *tfd)
{
}




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

  "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)
{
}


void direct2PsiDrawSortedPrimitives(int depth,long *tfv)
{
}



void direct2PsiDrawSortedPrimitivesPrelit(int depth,long *tfv)
{
}



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

		gte_ldv0(&world->meshdata->center);

		gte_rtps();

		modctrl->NormTop = world->meshdata->nortop;
		

		gte_stszotz(&world->depth);

		transformVertexListA(world->meshdata->vertop, world->meshdata->vern, tfv, tfd );

// 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 DIRECT_SCALE_DEPTH_DOWN
 //	{
 		for(i = 0; i < tfTotal; i++)
		{
			if(modctrl->depthoverride)
	 			tfdbase[i] = tfdbase[i] = modctrl->depthoverride;//>> DIRECT_SCALE_DEPTH_DOWN;
		}
 //	}
#endif


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

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


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

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

			direct2PsiDrawFlattenedPrimitivesDC((PSIMODEL *)psiData->modelName, tfvbase,tfdbase);

		}
		else
		{
			PSIMODEL *model;

			model = (PSIMODEL *)psiData->modelName;
			world = (PSIOBJECT*)psiData->objectTable[0];
		  
			direct2PsiDrawInsertionPrimitivesDC(model, tfvbase,tfdbase);
		}
	}
}





#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
	(char*)currentTextureList = (PSITEXTURES*)psiModel->textureOffset;

//	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);
	direct2PsiFixupPrimsDC(psiModel);

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