
#include "global.h"
#include "debris.h"
#include "lights.h"

#include "spotlite.h"

#define FLOOR_Y 0
//(10 << 3)

#define addPrimLen(ot,p,len,tempvar) \
{\
	tempvar = *(u_long *)(ot); \
    (*(u_long *)(ot) = (u_long)(p)); \
	tempvar |= (len<<24); \
	*(unsigned long *)(p) = tempvar; \
}
#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)\
)


#define SPOT_POINTS 16
#define MAP_SCALE_DEPTH_DOWN 3

// next little problemette.
// We want additive & transparent,
// which means putting a load of setmodes in...
/*
void spotliteDraw(SpotliteStr *spot)
{
	VECTOR delta;
	int dx,dz;
	VERT points[SPOT_POINTS+4];	// bigger array coz of the way txverts works
	int i;
	int ang;
	PACKET * packet;
	unsigned long *ot = currentDisplayPage->ot + 0;	// for the moment, just bung it at the front
	register long clipflag asm("$20");
	register long depth asm("$20");
	int t1,t2;
	long *tfv = transformedVertices;
	long *tfd = transformedDepths;


	delta.vx = spot->targ.vx - spot->org.vx;
	delta.vy = spot->targ.vy - spot->org.vy;
	delta.vz = spot->targ.vz - spot->org.vz;

// radius stays same on the ground regardless of height
// (erk, actually the ellipse orientation's not quite so simple...
	dx = spot->radius;	//(spot->radius * delta.vx) /delta.vy;
	dz = spot->radius;	//(spot->radius * delta.vz) /delta.vy;


	ang = 0;
	for(i = 0; i < SPOT_POINTS; i++)
	{
		points[i].vx = spot->targ.vx + ((rsin(ang) * dx) >>12);
		points[i].vy = spot->targ.vy;
		points[i].vz = spot->targ.vz + ((rcos(ang) * dz) >>12);
		ang += 4096/SPOT_POINTS;
	}
// don't forget to add the centrepoint
	points[SPOT_POINTS].vx = spot->targ.vx;
	points[SPOT_POINTS].vy = spot->targ.vy;
	points[SPOT_POINTS].vz = spot->targ.vz;

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

// Now run that little lot through transform...
	transformVertexListA(&points[0], SPOT_POINTS+1, transformedVertices, transformedDepths);



	packet = (PACKET *)currentDisplayPage->primPtr;


	spot->c_centre |= ((GPU_COM_G3 + 2) << 24);
//	spot->c_outer |= (GPU_COM_G3 << 24);

// Hurrah! Let's add some polygons


//	DB("spots....\n");
	for(i = 0; i < SPOT_POINTS; i++)
	{
		if(i==SPOT_POINTS-1)
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[0], tfv[i]);
		}
		else
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[i+1], tfv[i]);
		}
		gte_nclip();
		gte_stopz_cpu(clipflag);

		if (clipflag<0)
		{
			if(i==SPOT_POINTS-1)
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[0],tfd[i]);
			}
			else
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[i+1],tfd[i]);
			}
   			gte_avsz3();
			gte_stotz_cpu(depth);
			depth = depth >> MAP_SCALE_DEPTH_DOWN;
			depth -= 8;	// artificial sorting again - pull it closer to the camera

			if(depth < 0) depth = 0;
			if(depth > 1023) depth = 1023;

#define si ((POLY_G3*)packet)

	// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
			addPrimLen(ot+depth, (si),6,t2);
	// Copy the polygon data over into the OT
			t2 = *(u_long *) (&spot->c_centre);
			t1 = *(u_long *) (&spot->c_outer);
			*(u_long *)  (&si->r0) = t2;
			gte_stsxy3_g3(si);




//			DB("%d %d\n",si->x0,si->y0);

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

			si++;
//			packet = ADD2POINTER(packet,sizeof(POLY_G3));

#undef si

// now stick in an additive control
#define si ((DR_TPAGE*)packet)
			SetDrawTPage(si,1,1,(1<<5));	// (1 = +ve) this is a macro, anyway
			addPrimLen(ot+depth, (si),1,t2);
			si++;

#undef si

		}
	}


	currentDisplayPage->primPtr = (char *)packet;

}
*/
// x-size=(h * tan(r))
// z-size= (h * (tan(xa+r) - tan(xa-r)))
// ang = 0...4096 in steps
// {
// xpos = (x-size * sin(ang))
// zpos = (z-size * cos(ang))
// x =  xpos * cos(ya) + zpos * sin(ya)
// z = -xpos * sin(ya) + zpos * cos(ya)
// }

// x =  ((h * tan(r)) * sin(ang)) * cos(ya) + ((h * (tan(xa+r) - tan(xa-r))) * cos(ang)) * sin(ya)
// z = -((h * tan(r)) * sin(ang)) * sin(ya) + ((h * (tan(xa+r) - tan(xa-r))) * cos(ang)) * cos(ya)

// x =  ((h * tan(r)) * X) * cos(ya) + ((h * (tan(xa+r) - tan(xa-r))) * Z) * sin(ya)
// z = -((h * tan(r)) * X) * sin(ya) + ((h * (tan(xa+r) - tan(xa-r))) * Z) * cos(ya)



// Matrix Rot
// ((h * tan(r))  * cos(ya)   ,     (h * (tan(xa+r) - tan(xa-r))) * sin(ya)
// -((h * tan(r))) * sin(ya)  ,   ((h * (tan(xa+r) - tan(xa-r)))) * cos(ya)

// Matrix TX
// h * tan(xa) * cos(ya)
// h
// h * tan(xa) * sin(ya)


void PrintMatrix(MATRIX *mat2,char *id)
{
	DB("Mat %s=\n");
	DB("  %d %d %d\n",mat2->m[0][0],mat2->m[0][1],mat2->m[0][2]);
	DB("  %d %d %d\n",mat2->m[1][0],mat2->m[1][1],mat2->m[1][2]);
	DB("  %d %d %d\n",mat2->m[2][0],mat2->m[2][1],mat2->m[2][2]);
	DB("  %d %d %d\n",mat2->t[0],mat2->t[1],mat2->t[2]);
}


void spotliteMatrix(SpotliteStr *spot)
{
	MATRIX mat;
	static MATRIX mat2 = { {{4096,0,0}, {0,4096,0}, {0,0,4096}}, {0,0,0}};
	static MATRIX ident = { {{4096,0,0}, {0,4096,0}, {0,0,4096}}, {0,0,0}};
	int width;
	int length;
	int dx,dy,dz;
	int hyp;
	int xa,ya;
	int r = 256;	// spotlite angle

	dx = spot->targ.vx - spot->org.vx;
	dy = spot->targ.vy - spot->org.vy;
	dz = spot->targ.vz - spot->org.vz;

	ya = utilCalcAngle(dz,dx) & 4095;
	hyp = utilSqrt(dx*dx+dz*dz) >> 16;
	xa = utilCalcAngle(dy,hyp) & 4095;

	width = dy * rsin(r) / rcos(r);
	length = dy * rsin(xa+r) / rcos(xa+r) - dy * rsin(xa-r) / rcos(xa-r);

//	DB("dx,dz = %d %d\n",dx,dz);
//	DB("w,l = %d %d\n",width,length);

	width = 1000;
	length = 1000;

	mat.m[0][0] = (width * rcos(ya))>>12;
	mat.m[1][0] = 0;
	mat.m[2][0] = (width * rsin(ya))>>12;
	mat.m[0][1] = 0;
	mat.m[1][1] = 0;
	mat.m[2][1] = 0;
	mat.m[0][2] = (length * rcos(ya))>>12;
	mat.m[1][2] = 0;
	mat.m[2][2] = -(length * rsin(ya))>>12;

//	mat.t[0] = spot->org.vx + ((dy * rsin(xa)/rcos(xa) * rsin(ya)) >> 12);
	mat.t[0] = spot->targ.vx;
	mat.t[1] = spot->targ.vy;
	mat.t[2] = spot->targ.vz;
//	mat.t[2] = spot->org.vz + ((dy * rsin(xa)/rcos(xa) * rcos(ya)) >> 12);

// umm, no - ps matrices are translation post-rot
// ... meaning that we have to run that position through the matrix?


//	gte_MulMatrix0(&GsWSMATRIX, &mat, &mat2);
//	gte_SetRotMatrix(&mat2);
//	gte_SetTransMatrix(&mat2);

	mat.t[0] =0;
	mat.t[1] =0;
	mat.t[2] =0;

//	gte_MulMatrix0(&GsWSMATRIX, &mat, &mat2);
//	gte_SetRotMatrix(&mat2);
//	gte_SetTransMatrix(&mat2);


//	MulMatrix0(&GsWSMATRIX, &mat, &mat2);
	MulMatrix0(&mat, &GsWSMATRIX, &mat2);
	gte_SetRotMatrix(&mat);
	gte_SetTransMatrix(&mat);
	gte_ldlvl(&GsWSMATRIX.t);
	gte_rtirtr();
	gte_stlvl(&mat2.t);



// Okay, here's a full matrix multiply complete with offsets...
//	PrintMatrix(&GsWSMATRIX,"world");
//	PrintMatrix(&ident,"*ident");
/*
	MulMatrix0(&GsWSMATRIX, &ident, &mat2);
	gte_SetRotMatrix(&ident);
	gte_SetTransMatrix(&ident);
	gte_ldlvl(&GsWSMATRIX.t);
	gte_rtirtr();
	gte_stlvl(&mat2.t);
*/

//	PrintMatrix(&mat2,"Result");
	gte_SetRotMatrix(&mat2);
	gte_SetTransMatrix(&mat2);







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

}


// How to do it the slow way...
// get y,x circle
// tilt down by xa
// spin by ya
// project to floor
// run through GsWS

void spotliteProject(VERT *p, int ya, int xa)
{
	VERT t2;
}




void spotliteDraw(SpotliteStr *spot)
{
	VECTOR delta;
//	int dx,dz;
	VERT points[SPOT_POINTS+4];	// bigger array coz of the way txverts works
	int i;
	int ang;
	PACKET * packet;
	unsigned long *ot = currentDisplayPage->ot + 0;	// for the moment, just bung it at the front
	register long clipflag asm("$20");
	register long depth asm("$20");
	int t1,t2;
	long *tfv = transformedVertices;
	long *tfd = transformedDepths;

	spotliteMatrix(spot);

	ang = 0;
	for(i = 0; i < SPOT_POINTS; i++)
	{
		VERT temp;
		points[i].vx = rsin(ang);
		points[i].vy = 0;
		points[i].vz = rcos(ang);
		ang += 4096/SPOT_POINTS;
	}
// don't forget to add the centrepoint
	points[SPOT_POINTS].vx = 0;
	points[SPOT_POINTS].vy = 0;
	points[SPOT_POINTS].vz = 0;


// Now run that little lot through transform...
	transformVertexListA(&points[0], SPOT_POINTS+1, transformedVertices, transformedDepths);



	packet = (PACKET *)currentDisplayPage->primPtr;


	spot->c_centre |= ((GPU_COM_G3 + 2) << 24);
//	spot->c_outer |= (GPU_COM_G3 << 24);

// Hurrah! Let's add some polygons


//	DB("spots....\n");
	for(i = 0; i < SPOT_POINTS; i++)
	{
		if(i==SPOT_POINTS-1)
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[0], tfv[i]);
		}
		else
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[i+1], tfv[i]);
		}
		gte_nclip();
		gte_stopz_cpu(clipflag);

		if (clipflag<0)
		{
			if(i==SPOT_POINTS-1)
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[0],tfd[i]);
			}
			else
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[i+1],tfd[i]);
			}
   			gte_avsz3();
			gte_stotz_cpu(depth);
			depth = depth >> MAP_SCALE_DEPTH_DOWN;
			depth -= 8;	// artificial sorting again - pull it closer to the camera

			if(depth < 0) depth = 0;
			if(depth > 1023) depth = 1023;

#define si ((POLY_G3*)packet)

	// Put the polygon into the OT (Note that we re-use "depth" as one of the "t" variables)
			addPrimLen(ot+depth, (si),6,t2);
	// Copy the polygon data over into the OT
			t2 = *(u_long *) (&spot->c_centre);
			t1 = *(u_long *) (&spot->c_outer);
			*(u_long *)  (&si->r0) = t2;
			gte_stsxy3_g3(si);




//			DB("%d %d\n",si->x0,si->y0);

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

			si++;
//			packet = ADD2POINTER(packet,sizeof(POLY_G3));

#undef si

// now stick in an additive control
#define si ((DR_TPAGE*)packet)
			SetDrawTPage(si,1,1,(1<<5));	// (1 = +ve) this is a macro, anyway
			addPrimLen(ot+depth, (si),1,t2);
			si++;

#undef si

		}
	}


	currentDisplayPage->primPtr = (char *)packet;

}

