#include "..\specific\standard.h"
#include "..\specific\global.h"
#include "..\specific\hwrender.h"
#include "..\specific\hwinsert.h"
#include "..\specific\winmain.h"
#include "..\specific\debug.h"
#include "..\3dsystem\3d_gen.h"
#include "..\3dsystem\3dglodef.h"

#include "..\game\room.h"
#include "..\game\draw.h"
#include "..\game\effect2.h"
#include "..\game\lara.h"

#include "..\specific\init.h"

#include "..\specific\drawprimitive.h"
#include "..\specific\scanlines.h"


extern "C" int CurrentRoom;
extern int SqrTable[1024];
extern unsigned int RColorTable[33][33][33];
extern unsigned int GColorTable[33][33][33];
extern unsigned int BColorTable[33][33][33];


/*** locals ***/
local float LfAspectCorrection;

/************************************************************************************
************************************************************************************
*
*			Generate World-To-View Matrix given Position and Rotations
*
*			W2V is generated at Base Position on matrix stack...
*
************************************************************************************
************************************************************************************/
void	phd_GenerateW2V( PHD_3DPOS *viewpos )
{
	sint32	sx,sy,sz;
	sint32	cx,cy,cz;
	sint32	*mptr,*nptr;
	
	mptr = phd_mxptr = matrix_stack;    /* Put W2V at Top Of matrix Stack */
	nptr = w2v_matrix;
	
	sx = phd_sin( viewpos->x_rot );		/* get sin and cos values*/
	cx = phd_cos( viewpos->x_rot );
	sy = phd_sin( viewpos->y_rot );
	cy = phd_cos( viewpos->y_rot );
	sz = phd_sin( viewpos->z_rot );
	cz = phd_cos( viewpos->z_rot );
	
	*(nptr+M00) = *(mptr+M00) = (TRIGMULT3(sx, sy, sz) + TRIGMULT2(cy, cz));
	*(nptr+M01) = *(mptr+M01) = (TRIGMULT2(cx, sz));
	*(nptr+M02) = *(mptr+M02) = (TRIGMULT3(sx, cy, sz) - TRIGMULT2(sy, cz));
	*(nptr+M10) = *(mptr+M10) = (TRIGMULT3(sx, sy, cz) - TRIGMULT2(cy, sz));
	*(nptr+M11) = *(mptr+M11) = (TRIGMULT2(cx, cz));
	*(nptr+M12) = *(mptr+M12) = (TRIGMULT3(sx, cy, cz) + TRIGMULT2(sy, sz));
	*(nptr+M20) = *(mptr+M20) = (TRIGMULT2(cx, sy));
	*(nptr+M21) = *(mptr+M21) = -(sx);
	*(nptr+M22) = *(mptr+M22) = (TRIGMULT2(cx, cy));
	*(nptr+M03) = *(mptr+M03) = viewpos->x_pos;
	*(nptr+M13) = *(mptr+M13) = viewpos->y_pos;
	*(nptr+M23) = *(mptr+M23) = viewpos->z_pos;

	*(nptr+M10)=*(mptr+M10)=(sint32)(float(*(mptr+M10))*LfAspectCorrection);
	*(nptr+M11)=*(mptr+M11)=(sint32)(float(*(mptr+M11))*LfAspectCorrection);
	*(nptr+M12)=*(mptr+M12)=(sint32)(float(*(mptr+M12))*LfAspectCorrection);
}


extern "C" PHD_VECTOR	CamRot;
extern "C" unsigned long mGetAngle(long x1,long y1,long x2,long y2);
	
	/******************************************************************************
	*			Set Up W2V to look at specified Target
*****************************************************************************/
void	phd_LookAt( sint32 xsrc, sint32 ysrc, sint32 zsrc,
				    sint32 xtar, sint32 ytar, sint32 ztar,
				    PHD_ANGLE roll )
{
	PHD_ANGLE	angles[2];
	PHD_3DPOS 	viewer;
	
	phd_GetVectorAngles( xtar-xsrc,ytar-ysrc,ztar-zsrc, angles );
	viewer.x_pos = xsrc;
	viewer.y_pos = ysrc;
	viewer.z_pos = zsrc;
	viewer.x_rot = angles[1];
	viewer.y_rot = angles[0];
	viewer.z_rot = roll;

	
	int dx,dy,dz;
	dx = xsrc-xtar;
	dy = ysrc-ytar;
	dz = zsrc-ztar;

	int len = (int) sqrt((dx*dx)+(dz*dz));

	CamRot.x = (mGetAngle(0,0,len,dy) >> 4) & 4095 ;
	CamRot.y = (mGetAngle(zsrc,xsrc,ztar,xtar) >> 4) & 4095;
	CamRot.z = 0 ;
	
	phd_GenerateW2V( &viewer );
}

/******************************************************************************
*				Get Angles In 3D Space from Source to Dest
*					get Yaw angle at pos 0
*					  Pitch angle at pos 1
*****************************************************************************/
void	phd_GetVectorAngles( sint32 x, sint32 y, sint32 z, PHD_ANGLE *dest )
	{
	PHD_ANGLE	pitch;
	
	*(dest+0) = phd_atan( z, x );
	while  ( (sint16)x!=x || (sint16)y!=y || (sint16)z!=z  )
		{
		x >>= 2;
		y >>= 2;                /* Scale Coords Into Word Size */
		z >>= 2;
		}
	pitch = phd_atan( phd_sqrt( x*x + z*z ), y );
	if ( (y>0 && pitch>0) || (y<0 && pitch<0) )
		pitch = -pitch;
	*(dest+1) = pitch;
	}

	/******************************************************************************
	*					Normalise A Vector To Length 16384
*****************************************************************************/
void	phd_NormaliseVector( sint32 x, sint32 y, sint32 z, sint32 *dest )
	{
	sint32	len;
	
	if ( x || y || z )
		{
		while  ( (sint16)x!=x || (sint16)y!=y || (sint16)z!=z )
			{
			x >>= 2;        /* Scale Coords Into Word size values..*/
			y >>= 2;
			z >>= 2;
			}
		len = phd_sqrt( x*x + y*y + z*z );
		*(dest+0) = (x<<W2V_SHIFT)/len;
		*(dest+1) = (y<<W2V_SHIFT)/len;
		*(dest+2) = (z<<W2V_SHIFT)/len;
		}
	else
		{
		*(dest+0) = 0;		/* If Vector Is Zero Length*/
		*(dest+1) = 0;      /* then return Null Vector*/
		*(dest+2) = 0;
		}
	}

	/******************************************************************************
	*				Normalise And Regenerate Current Matrix
*****************************************************************************/
void	phd_NormaliseMatrix( void )
	{
	PHD_ANGLE	angles[3];
	
	phd_GetMatrixAngles( angles );
	phd_UnitMatrix();
	phd_RotYXZ( angles[1],angles[0],angles[2] );
	}

	/******************************************************************************
	*		  Get Rotation Angles From Current Matrix In ROT.ORDER ( Z,X,Y )
	*			saves in order rot_x, rot_y, rot_z
*****************************************************************************/
void	phd_GetMatrixAngles( PHD_ANGLE *dest )
	{
	PHD_ANGLE	roll,pitch,yaw;
	sint32		sy,cy;
	sint32		*mptr = phd_mxptr;
	
	yaw = phd_atan( *(mptr+M22), *(mptr+M02) );
	pitch = phd_atan( phd_sqrt( *(mptr+M22) * *(mptr+M22) + *(mptr+M02) * *(mptr+M02) ),
		*(mptr+M12) );
	sy = phd_sin( yaw );
	cy = phd_cos( yaw );
	roll = phd_atan( ( (cy * *(mptr+M00)) - (sy * *(mptr+M20)) ),
		( (sy * *(mptr+M21)) - (cy * *(mptr+M01)) ) );
	if ( ( *(mptr+M12)>=0 && pitch>0 ) || ( *(mptr+M12)<0 && pitch<0 ) )
		pitch = -pitch;
	
	*(dest+0) = pitch;             /* save RotX*/
	*(dest+1) = yaw;               /* save RotY*/
	*(dest+2) = roll;              /* save RotZ*/
	}

	/******************************************************************************
	******************************************************************************
	*		Rotate Current Matrix Around its X-axis
	******************************************************************************
*****************************************************************************/
void	phd_RotX( PHD_ANGLE rx )
	{
	sint32	sx,cx,r0,r1;
	sint32	*mptr;
	
	if ( rx )
		{
		mptr = phd_mxptr;
		sx = phd_sin(rx);
		cx = phd_cos(rx);
		
		r0 = *(mptr+M01)*cx + *(mptr+M02)*sx;
		r1 = *(mptr+M02)*cx - *(mptr+M01)*sx;
		*(mptr+M01) = r0>>W2V_SHIFT;
		*(mptr+M02) = r1>>W2V_SHIFT;
		
		r0 = *(mptr+M11)*cx + *(mptr+M12)*sx;
		r1 = *(mptr+M12)*cx - *(mptr+M11)*sx;
		*(mptr+M11) = r0>>W2V_SHIFT;
		*(mptr+M12) = r1>>W2V_SHIFT;
		
		r0 = *(mptr+M21)*cx + *(mptr+M22)*sx;
		r1 = *(mptr+M22)*cx - *(mptr+M21)*sx;
		*(mptr+M21) = r0>>W2V_SHIFT;
		*(mptr+M22) = r1>>W2V_SHIFT;
		}
	}

	/******************************************************************************
	******************************************************************************
	*		Rotate Current Matrix Around its Y-axis
	******************************************************************************
*****************************************************************************/
void	phd_RotY( PHD_ANGLE ry )
	{
	sint32	sy,cy,r0,r1;
	sint32	*mptr;
	
	if ( ry )
		{
		mptr = phd_mxptr;
		sy = phd_sin(ry);
		cy = phd_cos(ry);
		
		r0 = *(mptr+M00)*cy - *(mptr+M02)*sy;
		r1 = *(mptr+M02)*cy + *(mptr+M00)*sy;
		*(mptr+M00) = r0>>W2V_SHIFT;
		*(mptr+M02) = r1>>W2V_SHIFT;
		
		r0 = *(mptr+M10)*cy - *(mptr+M12)*sy;
		r1 = *(mptr+M12)*cy + *(mptr+M10)*sy;
		*(mptr+M10) = r0>>W2V_SHIFT;
		*(mptr+M12) = r1>>W2V_SHIFT;
		
		r0 = *(mptr+M20)*cy - *(mptr+M22)*sy;
		r1 = *(mptr+M22)*cy + *(mptr+M20)*sy;
		*(mptr+M20) = r0>>W2V_SHIFT;
		*(mptr+M22) = r1>>W2V_SHIFT;
		}
	}


	/******************************************************************************
	******************************************************************************
	*				Rotate Current Matrix Around its Z-axis
	******************************************************************************
*****************************************************************************/
void	phd_RotZ( PHD_ANGLE rz )
	{
	sint32	sz,cz,r0,r1;
	sint32	*mptr;
	
	if ( rz )
		{
		mptr = phd_mxptr;
		sz = phd_sin(rz);
		cz = phd_cos(rz);
		
		r0 = *(mptr+M00)*cz + *(mptr+M01)*sz;
		r1 = *(mptr+M01)*cz - *(mptr+M00)*sz;
		*(mptr+M00) = r0>>W2V_SHIFT;
		*(mptr+M01) = r1>>W2V_SHIFT;
		
		r0 = *(mptr+M10)*cz + *(mptr+M11)*sz;
		r1 = *(mptr+M11)*cz - *(mptr+M10)*sz;
		*(mptr+M10) = r0>>W2V_SHIFT;
		*(mptr+M11) = r1>>W2V_SHIFT;
		
		r0 = *(mptr+M20)*cz + *(mptr+M21)*sz;
		r1 = *(mptr+M21)*cz - *(mptr+M20)*sz;
		*(mptr+M20) = r0>>W2V_SHIFT;
		*(mptr+M21) = r1>>W2V_SHIFT;
		}
	}

	/******************************************************************************
	******************************************************************************
	*		Do Successive Rotations In Order Y-X-Z
	******************************************************************************
*****************************************************************************/
void	phd_RotYXZ( PHD_ANGLE ry, PHD_ANGLE rx, PHD_ANGLE rz )
	{
	sint32	sina,cosa,r0,r1;
	sint32	*mptr;
	
	mptr = phd_mxptr;
	if ( ry )
		{
		sina = phd_sin(ry);
		cosa = phd_cos(ry);
		r0 = *(mptr+M00)*cosa - *(mptr+M02)*sina;
		r1 = *(mptr+M02)*cosa + *(mptr+M00)*sina;
		*(mptr+M00) = r0>>W2V_SHIFT;
		*(mptr+M02) = r1>>W2V_SHIFT;
		r0 = *(mptr+M10)*cosa - *(mptr+M12)*sina;
		r1 = *(mptr+M12)*cosa + *(mptr+M10)*sina;
		*(mptr+M10) = r0>>W2V_SHIFT;
		*(mptr+M12) = r1>>W2V_SHIFT;
		r0 = *(mptr+M20)*cosa - *(mptr+M22)*sina;
		r1 = *(mptr+M22)*cosa + *(mptr+M20)*sina;
		*(mptr+M20) = r0>>W2V_SHIFT;
		*(mptr+M22) = r1>>W2V_SHIFT;
		}
	if ( rx )
		{
		sina = phd_sin(rx);
		cosa = phd_cos(rx);
		r0 = *(mptr+M01)*cosa + *(mptr+M02)*sina;
		r1 = *(mptr+M02)*cosa - *(mptr+M01)*sina;
		*(mptr+M01) = r0>>W2V_SHIFT;
		*(mptr+M02) = r1>>W2V_SHIFT;
		r0 = *(mptr+M11)*cosa + *(mptr+M12)*sina;
		r1 = *(mptr+M12)*cosa - *(mptr+M11)*sina;
		*(mptr+M11) = r0>>W2V_SHIFT;
		*(mptr+M12) = r1>>W2V_SHIFT;
		r0 = *(mptr+M21)*cosa + *(mptr+M22)*sina;
		r1 = *(mptr+M22)*cosa - *(mptr+M21)*sina;
		*(mptr+M21) = r0>>W2V_SHIFT;
		*(mptr+M22) = r1>>W2V_SHIFT;
		}
	if ( rz )
		{
		sina = phd_sin(rz);
		cosa = phd_cos(rz);
		r0 = *(mptr+M00)*cosa + *(mptr+M01)*sina;
		r1 = *(mptr+M01)*cosa - *(mptr+M00)*sina;
		*(mptr+M00) = r0>>W2V_SHIFT;
		*(mptr+M01) = r1>>W2V_SHIFT;
		r0 = *(mptr+M10)*cosa + *(mptr+M11)*sina;
		r1 = *(mptr+M11)*cosa - *(mptr+M10)*sina;
		*(mptr+M10) = r0>>W2V_SHIFT;
		*(mptr+M11) = r1>>W2V_SHIFT;
		r0 = *(mptr+M20)*cosa + *(mptr+M21)*sina;
		r1 = *(mptr+M21)*cosa - *(mptr+M20)*sina;
		*(mptr+M20) = r0>>W2V_SHIFT;
		*(mptr+M21) = r1>>W2V_SHIFT;
		}
	}

	/******************************************************************************
	******************************************************************************
	*		Do Successive Rotations In Order Y-X-Z
	*			Rotations Are packed In Long Word.. 10 Bits Each
	*				RotX = Bits 29..20
	*				RotY = Bits 19..10
	*				RotZ = Bits 09..00
	******************************************************************************
*****************************************************************************/
void	phd_RotYXZpack( sint32	rots )
	{
	sint32		ang;
	sint32		sina,cosa,r0,r1;
	sint32		*mptr;
	
	mptr = phd_mxptr;
	ang = (((rots>>10)&1023)<<6);		// Extract RotY
	if ( ang )
		{
		sina = phd_sin(ang);
		cosa = phd_cos(ang);
		r0 = *(mptr+M00)*cosa - *(mptr+M02)*sina;
		r1 = *(mptr+M02)*cosa + *(mptr+M00)*sina;
		*(mptr+M00) = r0>>W2V_SHIFT;
		*(mptr+M02) = r1>>W2V_SHIFT;
		r0 = *(mptr+M10)*cosa - *(mptr+M12)*sina;
		r1 = *(mptr+M12)*cosa + *(mptr+M10)*sina;
		*(mptr+M10) = r0>>W2V_SHIFT;
		*(mptr+M12) = r1>>W2V_SHIFT;
		r0 = *(mptr+M20)*cosa - *(mptr+M22)*sina;
		r1 = *(mptr+M22)*cosa + *(mptr+M20)*sina;
		*(mptr+M20) = r0>>W2V_SHIFT;
		*(mptr+M22) = r1>>W2V_SHIFT;
		}
	ang = (((rots>>20)&1023)<<6);		// Extract RotX
	if ( ang )
		{
		sina = phd_sin(ang);
		cosa = phd_cos(ang);
		r0 = *(mptr+M01)*cosa + *(mptr+M02)*sina;
		r1 = *(mptr+M02)*cosa - *(mptr+M01)*sina;
		*(mptr+M01) = r0>>W2V_SHIFT;
		*(mptr+M02) = r1>>W2V_SHIFT;
		r0 = *(mptr+M11)*cosa + *(mptr+M12)*sina;
		r1 = *(mptr+M12)*cosa - *(mptr+M11)*sina;
		*(mptr+M11) = r0>>W2V_SHIFT;
		*(mptr+M12) = r1>>W2V_SHIFT;
		r0 = *(mptr+M21)*cosa + *(mptr+M22)*sina;
		r1 = *(mptr+M22)*cosa - *(mptr+M21)*sina;
		*(mptr+M21) = r0>>W2V_SHIFT;
		*(mptr+M22) = r1>>W2V_SHIFT;
		}
	ang = ((rots&1023)<<6);				// Extract RotZ
	if ( ang )
		{
		sina = phd_sin(ang);
		cosa = phd_cos(ang);
		r0 = *(mptr+M00)*cosa + *(mptr+M01)*sina;
		r1 = *(mptr+M01)*cosa - *(mptr+M00)*sina;
		*(mptr+M00) = r0>>W2V_SHIFT;
		*(mptr+M01) = r1>>W2V_SHIFT;
		r0 = *(mptr+M10)*cosa + *(mptr+M11)*sina;
		r1 = *(mptr+M11)*cosa - *(mptr+M10)*sina;
		*(mptr+M10) = r0>>W2V_SHIFT;
		*(mptr+M11) = r1>>W2V_SHIFT;
		r0 = *(mptr+M20)*cosa + *(mptr+M21)*sina;
		r1 = *(mptr+M21)*cosa - *(mptr+M20)*sina;
		*(mptr+M20) = r0>>W2V_SHIFT;
		*(mptr+M21) = r1>>W2V_SHIFT;
		}
	}




	/******************************************************************************
	******************************************************************************
	*				Translate Matrix to Relative Position
	******************************************************************************
*****************************************************************************/
int		phd_TranslateRel( sint32 x, sint32 y, sint32 z )
	{
	sint32	*mptr;
	mptr = phd_mxptr;
	*(mptr+M03) += *(mptr+M00)*x + *(mptr+M01)*y + *(mptr+M02)*z;
	*(mptr+M13) += *(mptr+M10)*x + *(mptr+M11)*y + *(mptr+M12)*z;
	*(mptr+M23) += *(mptr+M20)*x + *(mptr+M21)*y + *(mptr+M22)*z;

	if ( (x = ABS(*(mptr+M03))) > phd_zfar ||
		(y = ABS(*(mptr+M13))) > phd_zfar ||
		(z = ABS(*(mptr+M23))) > phd_zfar )
		return(0);
	return(1);
	}

	/******************************************************************************
	******************************************************************************
	*				Translate Matrix to Absolute Position
	*					returns 0 if out of range!!!
	******************************************************************************
*****************************************************************************/


void	phd_TranslateAbs( sint32 x, sint32 y, sint32 z )
	{
	sint32	*mptr = phd_mxptr;

	x -= *(w2v_matrix+M03);          /* Subtract Position Of */
	y -= *(w2v_matrix+M13);          /* W2V Matrix  */
	z -= *(w2v_matrix+M23);

	*(mptr+M03) = *(mptr+M00)*x + *(mptr+M01)*y + *(mptr+M02)*z;
	*(mptr+M13) = *(mptr+M10)*x + *(mptr+M11)*y + *(mptr+M12)*z;
	*(mptr+M23) = *(mptr+M20)*x + *(mptr+M21)*y + *(mptr+M22)*z;
	}



/*****************************************************************************
*****************************************************************************
*				Insert Polygon Object polygons Into Draw List
*				using Current matrix pointed to by phd_mxptr
*****************************************************************************
****************************************************************************/
void	phd_PutPolygons( sint16 *objptr, int clipstatus )
{
	objptr+=4;          								  // Skip X,Y,Z,Radius
	phd_leftfloat   = (float)phd_winxmin;
	phd_topfloat    = (float)phd_winymin;
	phd_rightfloat  = (float)(phd_winxmin + phd_winxmax + 1);
	phd_bottomfloat = (float)(phd_winymin + phd_winymax + 1);
	f_centerx       = (float)(phd_winxmin + phd_centerx);
	f_centery       = (float)(phd_winymin + phd_centery);

	sint16 *objptr1;
	objptr1 = objptr;
	
	objptr = calc_object_vertices(objptr);
	if (objptr)
	{
		objptr=calc_vertice_light(objptr,objptr1);
		objptr=InsertObjectGT4(objptr+1,*(objptr),MID_SORT);
		objptr=InsertObjectGT3(objptr+1,*(objptr),MID_SORT);
		objptr=InsertObjectG4 (objptr+1,*(objptr),MID_SORT);
		objptr=InsertObjectG3 (objptr+1,*(objptr),MID_SORT);
	}
}

/******************************************************************************
*				Do Room Insertion
*****************************************************************************/



void S_InsertRoom(sint16 *objptr, int outside)
{
	phd_leftfloat   = (float)(phd_winxmin + phd_left);
	phd_topfloat    = (float)(phd_winymin + phd_top);
	phd_rightfloat  = (float)(phd_winxmin + phd_right + 1);
	phd_bottomfloat = (float)(phd_winymin + phd_bottom + 1);
	f_centerx       = (float)(phd_winxmin + phd_centerx);
	f_centery       = (float)(phd_winymin + phd_centery);
		
	objptr = calc_roomvert(objptr, outside ? 0:16);
	objptr = InsertObjectGT4(objptr+1,*(objptr),FAR_SORT); //rf
	objptr = InsertObjectGT3(objptr+1,*(objptr),FAR_SORT); // rf
	objptr = ins_room_sprite(objptr+1,*(objptr));

}

	/*****************************************************************************
	*		Calculate Background Object
****************************************************************************/



sint16 *calc_back_light( sint16 *objptr )
{
	int			numvert;
	PHD_VBUF 	*dest;
	
	/* Allow for prelit or dynamic lit info (but ignore either way) */
	numvert = (int)*(objptr++);
	if (numvert > 0)
		objptr += numvert * 3;
	else if (numvert < 0)
		{
		numvert = -numvert;
		objptr += numvert;
		}
	
	/* Set shade to mid-shade for moment... */
	for (dest = vbuf; numvert>0; numvert--, dest++)
		dest->g = 16<<10|16<<5|16; //0x0fff;
	
	return( objptr );
}




void S_InsertBackground(sint16 *objptr)
{
	objptr += 4;
	
	phd_leftfloat   = (float)(phd_winxmin + phd_left);
	phd_topfloat    = (float)(phd_winymin + phd_top);
	phd_rightfloat  = (float)(phd_winxmin + phd_right + 1);
	phd_bottomfloat = (float)(phd_winymin + phd_bottom + 1);
	f_centerx       = (float)(phd_winxmin + phd_centerx);
	f_centery       = (float)(phd_winymin + phd_centery);

	objptr = calc_object_vertices(objptr);
	
	if (objptr)
	{
		HWR_EnableZBuffer(false,false);
		
		objptr = calc_back_light(objptr); 
		objptr = InsertObjectGT4(objptr+1,*(objptr),BACK_SORT);
		objptr = InsertObjectGT3(objptr+1,*(objptr),BACK_SORT);
		objptr = InsertObjectG4 (objptr+1,*(objptr),BACK_SORT);
		objptr=  InsertObjectG3 (objptr+1,*(objptr),BACK_SORT);
		
		if(App.lpZBuffer)
		{
			HWR_EnableZBuffer(false,false);
			HWR_EnableColorKey(false);
			DrawBuckets(0);
			InitBuckets();
		}
		else
		{			
			bForceAffineMap = 1;
			HWR_DrawPolyList(surfacenumbf,(sint32*) sort3d_bufferbf);
			bForceAffineMap = 0;
		}

		phd_InitPolyList();
		
		HWR_EnableZBuffer(true,true);	

	}



}

void S_InsertInvBgnd(sint16 *objptr)
{
}

/*****************************************************************************
*		Calculate Vertices With Possible Z,Xs,Ys Clipping: Assumes water effect
****************************************************************************/

extern "C" int CurrentMesh;
/*
sint16	*calc_object_vertices( sint16 *objptr )
	{
	PHD_VBUF 	*dest;
	sint32		*mptr;
	float		xv,yv,zv, xs,ys;
	int			numvert;
	sint8		clipflag, totclip;
#ifdef BETTER_ROOM_SORT
	float f_mid_sort;

	f_mid_sort = App.lpZBuffer ? 0.0f : (float)(mid_sort<<W2V_SHIFT+8);
#endif
	
	mptr = phd_mxptr;
	dest = vbuf;
	totclip = -1;
	objptr++;
	for ( numvert = (int)*(objptr++); numvert>0; numvert--,dest++,objptr+=3 )
		{
		dest->xv = xv = float(mptr[M00] * objptr[0] + mptr[M01] * objptr[1] + mptr[M02] * objptr[2] + mptr[M03]);
		dest->yv = yv = float(mptr[M10] * objptr[0] + mptr[M11] * objptr[1] + mptr[M12] * objptr[2] + mptr[M13]);
		dest->z = mptr[M20] * objptr[0] + mptr[M21] * objptr[1] + mptr[M22] * objptr[2] + mptr[M23];
		
		zv = float(dest->z);

		if (zv >= f_znear)
			{
			if (zv>=f_zfar)
				dest->zv=f_zfar;		// probably don't need to add f_mid_sort if its already at f_zfar...
			else
#ifdef BETTER_ROOM_SORT
				dest->zv=zv+f_mid_sort;
#else
				dest->zv=zv;
#endif
			zv = f_persp / zv;
			dest->xs = xs = xv * zv + f_centerx;
			dest->ys = ys = yv * zv + f_centery;
			dest->ooz=zv*f_oneopersp;
			clipflag = 0;
			if ( xs<phd_leftfloat )
				clipflag++;
			else if ( xs>phd_rightfloat )
				clipflag += 2;
			if ( ys<phd_topfloat )
				clipflag += 4;
			else if ( ys>phd_bottomfloat )
				clipflag += 8;
			}
		else
			{
			dest->zv=zv;
			clipflag = -128;				//  flag we need Z-clipping
			}
		dest->clip = clipflag;
		totclip &= clipflag;

	#ifdef GAMEDEBUG
		ObjVerts++;
	#endif

	}
	if (totclip)
		return (NULL);


	
	return(objptr);       				// return current objptr.
	}
*/




/*****************************************************************************
*			Calculate Room Vertices And Perform Depth Cueing..
****************************************************************************/
#ifdef BETTER_ROOM_SORT
sint32 mid_sort;
#endif


/*
sint16	*calc_roomvert( sint16 *objptr, int far_clip )
{
	PHD_VBUF 	*dest;
	sint32		*mptr;
	int			numvert;
	sint8		clipflag;
	int n;
	float		zv;
	sint32 z;
	int dist;
	unsigned long mod;
	int x1,y1,z1;
	bool lit;
	unsigned int r,g,b;	
	ROOM_INFO *this_room;

	this_room = &room[CurrentRoom];

	mptr = phd_mxptr;
	dest = vbuf;

#ifdef BETTER_ROOM_SORT
	float f_mid_sort;
	f_mid_sort = App.lpZBuffer ? 0.0f : (float)(mid_sort<<W2V_SHIFT+8);
#endif

	for(numvert=(int)*(objptr++);numvert>0;numvert--,dest++,objptr+=6)
	{
		dest->g  = objptr[5];

		dest->xv = float(mptr[M00]) * float(objptr[0]) + float(mptr[M01]) * float(objptr[1]) + float(mptr[M02]) * float(objptr[2]) + float(mptr[M03]);
		dest->yv = float(mptr[M10]) * float(objptr[0]) + float(mptr[M11]) * float(objptr[1]) + float(mptr[M12]) * float(objptr[2]) + float(mptr[M13]);
		 	  zv = float(mptr[M20]) * float(objptr[0]) + float(mptr[M21]) * float(objptr[1]) + float(mptr[M22]) * float(objptr[2]) + float(mptr[M23]);

		z = FTOL(zv);		

//		dest->xv = (float)(mptr[M00] * objptr[0] + mptr[M01] * objptr[1] + mptr[M02] * objptr[2] + mptr[M03]);
//		dest->yv = (float)(mptr[M10] * objptr[0] + mptr[M11] * objptr[1] + mptr[M12] * objptr[2] + mptr[M13]);
//		z = (mptr[M20] * objptr[0] + mptr[M21] * objptr[1] + mptr[M22] * objptr[2] + mptr[M23]);

		
//		dest->zv = zv = float(z);

		dest->zv = zv;

		if (zv>=f_znear)
		{
			clipflag = 0;
			zv		 = f_persp/zv;
	#ifdef BETTER_ROOM_SORT
			dest->zv += f_mid_sort;
#endif		
			if (zv<=DPQ_E) 	
				dest->ooz	= zv*f_oneopersp;
			else
			{
				clipflag    = far_clip;								
				dest->zv	= f_zfar;	
				dest->ooz	= f_oneozfar; 
			}


			r = dest->g&0x7c00;
			g = dest->g&0x03e0;
			b = dest->g&0x001f;

			lit = false;

			for(n=0;n<number_dynamics;n++)
			{
				if(dynamics[n].on)
				{
					z1 = (dynamics[n].z - (objptr[2]+this_room->z))>>7;
			
					mod = (z1*z1);
				
					if(mod < 1024)
					{
						x1 = (dynamics[n].x - (objptr[0]+this_room->x))>>7;
	
						mod +=(x1*x1);

						if(mod < 1024)
						{
				            y1 = (dynamics[n].y - (objptr[1]+this_room->y))>>7;
				
							mod += (y1*y1);

							if(mod < 1024)					
							{
								dist = SqrTable[mod];

								if(dist < dynamics[n].falloff>>8)
								{
									r += RColorTable[dynamics[n].falloff>>8][dist][dynamics[n].r];
									g += GColorTable[dynamics[n].falloff>>8][dist][dynamics[n].g];
									b += BColorTable[dynamics[n].falloff>>8][dist][dynamics[n].b];
									lit = true;
								}
							}
						}	
					}
				}
			}
		
			if(lit)
			{
				if(r>0x7c00) r = 0x7c00;
				if(g>0x03e0) g = 0x03e0;
				if(b>0x001f) b = 0x001f;
				dest->g = r|g|b;
			}
		
			int off;

			off = ((objptr[0]+this_room->x)>>6) +
				  ((objptr[1]+this_room->y)>>6) +
				  ((objptr[2]+this_room->z)>>7);
			
			off = off&0xfc;
			
			if(objptr[4]&(1<<13))
			{
				dest->yv += float(WaterTable[this_room->MeshEffect][((WaterTable[this_room->MeshEffect][off&63].random+(wibble>>2))&63)].choppy<<W2V_SHIFT);
				
				int r,g,b;
				int shade;
			
				r = ((dest->g>>10)&0x1f);
				g = ((dest->g>>5)&0x1f);
				b = ((dest->g)&0x1f);
			
				shade = -WaterTable[this_room->MeshEffect][((WaterTable[this_room->MeshEffect][off&63].random+(wibble>>2))&63)].choppy>>4;

				r+= shade;
				g+= shade;
				b+= shade;
				
				if(r>0x1f) r = 0x1f;
				if(g>0x1f) g = 0x1f;
				if(b>0x1f) b = 0x1f;
				if(r<0) r = 0;
				if(g<0) g = 0;
				if(b<0) b = 0;
				dest->g = r<<10|g<<5|b;
			}

			
			dest->xs =  dest->xv * zv + f_centerx;
			dest->ys =  dest->yv * zv + f_centery;
				
			if(objptr[4]&(1<<14))
			{			
				int r,g,b;
				int shade;
			
				r = ((dest->g>>10)&0x1f);
				g = ((dest->g>>5)&0x1f);
				b = ((dest->g)&0x1f);
				
				shade = WaterTable[this_room->MeshEffect][((WaterTable[this_room->MeshEffect][off&63].random+(wibble>>2))&63)].shimmer;
				shade +=WaterTable[this_room->MeshEffect][((WaterTable[this_room->MeshEffect][off&63].random+(wibble>>2))&63)].abs;
	
				r+= shade;
				g+= shade;
				b+= shade;
			
				if(r>0x1f) r = 0x1f;
				if(g>0x1f) g = 0x1f;
				if(b>0x1f) b = 0x1f;
				if(r<0) r = 0;
				if(g<0) g = 0;
				if(b<0) b = 0;
				dest->g = r<<10|g<<5|b;
			}
							
			if (water_effect && objptr[4]>=0)
				dest->ys += wibble_table[(FTOL(dest->xs) + wibble>>3) & (WIBBLE_SIZE-1)];
			
			if(dest->xs<phd_leftfloat)
				clipflag++;
			else if(dest->xs>phd_rightfloat)
				clipflag += 2;

			if(dest->ys<phd_topfloat)
				clipflag += 4;
			else if(dest->ys>phd_bottomfloat)
				clipflag += 8;
			
			dest->clip = clipflag;          
			
		}
		else
			dest->clip = -128;				// flag we need Z-clipping
	

		// Apply Depth Q
	
		if (z>DPQ_S)
		{
			int r,g,b;
			int v;
			r = ((dest->g>>10)&0x1f)<<3;
			g = ((dest->g>>5)&0x1f)<<3;
			b = (dest->g&0x1f)<<3;
			v = 2048-((z - DPQ_S)>>16);	
			r *= v;
			g *= v;
			b *= v;
			r >>= 14;
			g >>= 14;
			b >>= 14;
			if(r<0) r= 0;	
			if(g<0) g= 0;	
			if(b<0) b= 0;	
			dest->g = r<<10|g<<5|b;
		}

#ifdef GAMEDEBUG
		RoomVerts++;
#endif
	
	}

	return(objptr);       				// return current objptr.
}

*/


	/*****************************************************************************
	*		Position Global Light Vector In World Using Angles
****************************************************************************/
void	phd_RotateLight( PHD_ANGLE pitch, PHD_ANGLE yaw )
	{
	PHD_VECTOR	ls_world;
	sint32		*mptr;
	sint32		cx;
	
	phd_ls_yaw = yaw;
	phd_ls_pitch = pitch;
	
	cx = (sint32)phd_cos(pitch);
	ls_world.x = TRIGMULT2( cx, (sint32)phd_sin(yaw) );	  // Calc World Frame
	ls_world.y = -phd_sin(pitch);                         // Light Vector
	ls_world.z = TRIGMULT2( cx, (sint32)phd_cos(yaw) );
	
	mptr = w2v_matrix;
	ls_vector_view.x = ( *(mptr+M00) * ls_world.x		  // Rotate Into
					   + *(mptr+M01) * ls_world.y         // View Frame..
					   + *(mptr+M02) * ls_world.z ) >> W2V_SHIFT;
	ls_vector_view.y = ( *(mptr+M10) * ls_world.x
					   + *(mptr+M11) * ls_world.y
					   + *(mptr+M12) * ls_world.z ) >> W2V_SHIFT;
	ls_vector_view.z = ( *(mptr+M20) * ls_world.x
					   + *(mptr+M21) * ls_world.y
					   + *(mptr+M22) * ls_world.z ) >> W2V_SHIFT;
	}

	/**************************************************************************
	*		Position Global Light Vector In World Using Destination Position
	*				and Position of Light...
*************************************************************************/
void	phd_PointLight( PHD_3DPOS *destpos, PHD_3DPOS *lightpos )
	{
	PHD_ANGLE	angles[2];
	
	phd_GetVectorAngles( destpos->x_pos-lightpos->x_pos,  // Get Angles
		destpos->y_pos-lightpos->y_pos,  // Of Light
		destpos->z_pos-lightpos->z_pos,  // Vector
		angles );
	phd_RotateLight( angles[1],angles[0] );
	}


	/*****************************************************************************
	*			Initialise Polygon output stuff
****************************************************************************/
void	phd_InitPolyList( void )
{
	surfacenum   = 0;                 		
	surfacenumbf = 0;
	
	sort3dptr = (sint32*) sort3d_buffer;	
	info3dptr = (sint16*) info3d_buffer;    

	info3dptrbf = (sint16*) info3d_bufferbf;
	sort3dptrbf = (sint32*) sort3d_bufferbf;
		
	HWR_InitVertexList();
}

/******************************************************************************
*   	   Sort The Polygon Draw List In Back To Front Order
*****************************************************************************/

void	phd_SortPolyList( int number,sint32 buffer[][10])
{
	sint32	i;
	if (number)
	{
		for (i=0; i<number;i++)         // eliminate polygon flicker
			buffer[i][1] += i;
		
		do_quickysorty( 0, number-1, buffer);
	}
}

/******************************************************************************
*   	   Quick Sort Routine  ( Pisses on Shell sort !!! )
*								 but uses recursion!!!!
*****************************************************************************/

void	do_quickysorty( int left, int right, sint32 buffer[][10] )
{
	sint32		i,j;
	sint32		compare,swap;
	
	i = left;
	j = right;
	compare = buffer[(left+right)/2][1]; 		/* get middle value*/

	do
	{
		while (buffer[i][1]>compare && i<right)
			++i;  									/* was <x*/
		while ( compare>buffer[j][1] && j>left )
			--j;  									/* was x<*/
		if ( i<=j ) 									/* was ( i<=j )*/
		{
			swap = buffer[i][1];
			buffer[i][1] = buffer[j][1];	/* swap elements*/
			buffer[j][1] = swap;

			swap = buffer[i][0];
			buffer[i][0] = buffer[j][0];
			buffer[j][0] = swap;
						
			i++;
			j--;
		}
	}
	while (i<=j);
		
	if(left<j)
		do_quickysorty(left,j,buffer);
	if(i<right)
		do_quickysorty(i,right,buffer);
}


	/*****************************************************************************
	*			Print Polygon output list
****************************************************************************/
void	phd_PrintPolyList(void* ptr)
	{
	/* Draw onto render surface */
	int		i;
	sint32	*sptr;
	sint16	*iptr;
	sint16	routine;
	
	phd_winptr = (char *)ptr;
	
	sptr = (sint32 *)sort3d_buffer;
	for ( i=surfacenum; i>0; i-- )
		{
		iptr = (sint16 *)( *sptr );
		routine = *(iptr++);
		(*poly_draw_routines[routine])( iptr );
		sptr += 2;
		}
	}




	/*****************************************************************************
	*			Alter FOV whilst retaining Other Screen attributes
	*				Angle is in PHD_ANGLE format
****************************************************************************/
void	AlterFOV( PHD_ANGLE fov )
{
	fov/=2;              		
	phd_persp=((phd_winwidth/2)*phd_cos(fov))/phd_sin(fov);
//	phd_persp=(((phd_winwidth+phd_winheight)/4)*phd_cos(fov))/phd_sin(fov);

	phd_oopersp = sint32(one)/phd_persp;
	
	f_persp=float(phd_persp);
	
	f_oneopersp=one/f_persp;
	f_perspoznear=f_persp/f_znear;
	float fAspect=float(phd_winwidth)/float(phd_winheight);
	float fDesiredAspect=4.0f/3.0f;

/*	if (!G_AppSettings.tFullScreen && G_AppSettings.nAspectMode==ASPECT_16_9)*/
	//	fDesiredAspect=16.0f/9.0f; //RF
	
	LfAspectCorrection=fDesiredAspect/fAspect;
}

#define MINZR (0.005f)
#define MAXZR (0.995f)
#define ZRANGE (MAXZR-MINZR)

void SetupZRange(void)
	{
	f_b=(ZRANGE*f_znear*f_zfar)/(f_znear-f_zfar);
	f_a=MINZR-(f_b/f_znear);
	f_b=-f_b;
	f_boo=f_b/one;
	}

void SetZNear(int nNewZNear)
	{
	phd_znear=nNewZNear;
	f_znear=float(phd_znear);
	f_oneoznear=one/f_znear;
	f_perspoznear=f_persp/f_znear;
	SetupZRange();
	}

void SetZFar(int nNewZFar)
	{
	phd_zfar=nNewZFar;
	f_zfar=float(phd_zfar);
	SetupZRange();
	}

	/*****************************************************************************
	*					Initialise 3D Window
****************************************************************************/
void	phd_InitWindow( int  x, 	int y,
					   int  width, int height,
					   int  nearz, int farz,
					   int  view_angle,
					   int  scrwidth,
					   int	 scrheight)
					   
	{
	
	Log("phd_InitWindow x %d y %d w %d h %d nz %d fz %d va %d sw %d sh %d",x,y,width,height,nearz,farz,view_angle,scrwidth,scrheight);
	
	phd_winxmin = x;
	phd_winymin = y;
	phd_winxmax = width-1;
	phd_winymax = height-1;
	phd_winwidth = width;
	phd_winheight = height;
	phd_centerx = width/2;
	phd_centery = height/2;
	phd_znear = nearz<<W2V_SHIFT;
	phd_zfar = farz<<W2V_SHIFT;
	phd_viewdist = farz;
	phd_scrwidth = scrwidth;
	phd_scrheight = scrheight;

	f_centerx=float(phd_centerx);
	f_centery=float(phd_centery);

	AlterFOV(view_angle*ONE_DEGREE);
	SetZNear(phd_znear);
	SetZFar(phd_zfar);

	InitZTable();

	phd_left = 0;
	phd_top = 0;
	phd_right = phd_winxmax;
	phd_bottom = phd_winymax;
	phd_mxptr = matrix_stack;

	GrcDumpWindow.left=phd_winxmin;
	GrcDumpWindow.top=phd_winymin;
	GrcDumpWindow.right=phd_winxmin+phd_winwidth;
	GrcDumpWindow.bottom=phd_winymin+phd_winheight;

	switch (App.nRenderMode)	//RF
		{
		case RENDERER_INTERNAL:
			/*
			InsertObjectGT3=ins_objectGT3;
			InsertObjectGT4=ins_objectGT4;
			InsertObjectG3=ins_objectG3;
			InsertObjectG4=ins_objectG4;
			InsertSprite=insert_sprite;
			InsertFlatRect=ins_flat_rect;
			InsertLine=ins_line;
			InsertTrans8=ins_poly_trans8;
			InsertTransQuad=ins_trans_quad;
			perspective_distance = (G_AppSettings.tPerspectiveCorrect)? ((6*WALL_L)<<W2V_SHIFT) : ((3*WALL_L)<<W2V_SHIFT);
			*/
			
				InsertObjectGT3=HWI_InsertObjectGT3_Sorted;
				InsertObjectGT4=HWI_InsertObjectGT4_Sorted;
				InsertObjectG3=HWI_InsertObjectG3_Sorted;
				InsertObjectG4=HWI_InsertObjectG4_Sorted;
				InsertFlatRect=HWI_InsertFlatRect_Sorted;
				InsertLine=HWI_InsertLine_Sorted;
				RoomInsertObjectGT3 = HWI_InsertObjectGT3_Sorted;
				RoomInsertObjectGT4 = HWI_InsertObjectGT4_Sorted;
				InsertSprite=HWI_InsertSprite_Sorted;			// color keyed so sorted
				InsertTrans8=HWI_InsertTrans8_Sorted;
				InsertTransQuad=HWI_InsertTransQuad_Sorted;
			break;

		case RENDERER_HAL:
//			if (App.lpZBuffer)
//			{
//				InsertObjectGT3=HWI_RoomInsertObjectGT3_ZBuffered;
//				InsertObjectGT4=HWI_RoomInsertObjectGT4_ZBuffered;
//				InsertObjectG3=HWI_InsertObjectG3_ZBuffered;
//				InsertObjectG4=HWI_InsertObjectG4_ZBuffered;
//				InsertFlatRect=HWI_InsertFlatRect_ZBuffered;
//				InsertLine=HWI_InsertLine_ZBuffered;
//				RoomInsertObjectGT3 = HWI_RoomInsertObjectGT3_ZBuffered;
//				RoomInsertObjectGT4 = HWI_RoomInsertObjectGT4_ZBuffered;
//			}
//			else
//			{
				InsertObjectGT3=HWI_InsertObjectGT3_Sorted;
				InsertObjectGT4=HWI_InsertObjectGT4_Sorted;
				InsertObjectG3=HWI_InsertObjectG3_Sorted;
				InsertObjectG4=HWI_InsertObjectG4_Sorted;
				InsertFlatRect=HWI_InsertFlatRect_Sorted;
				InsertLine=HWI_InsertLine_Sorted;
				RoomInsertObjectGT3 = HWI_InsertObjectGT3_Sorted;
				RoomInsertObjectGT4 = HWI_InsertObjectGT4_Sorted;
//			}
			
			InsertSprite=HWI_InsertSprite_Sorted;			// color keyed so sorted
			InsertTrans8=HWI_InsertTrans8_Sorted;
			InsertTransQuad=HWI_InsertTransQuad_Sorted;
			break;
		}
	}






