/*********************************************************************************************/
/*                                                                                           */
/* Draw Phase                                                                                */
/*                                                                                           */
/*********************************************************************************************/

#include "game.h"

#ifdef PSX_VERSION
#include "..\spec_psx\maths.h"
#endif

/*---------------------------------------------------------------------------
 * Constants
\*--------------------------------------------------------------------------*/

//#define OLD_PRINT_ROOMS
//#define PRINT_DRAWN_ROOMS
#define CLIP_OUTSIDE

/*---------------------------------------------------------------------------
 * Externals
\*--------------------------------------------------------------------------*/

extern long wibble;

#ifdef PSX_VERSION
extern void DrawRoomletListAsm(LONG);
extern void GetRoomBoundsAsm(LONG);
#endif

/*---------------------------------------------------------------------------
 * Globals
\*--------------------------------------------------------------------------*/

sint16 number_rooms;
ROOM_INFO *room;

OBJECT_INFO objects[NUMBER_OBJECTS];
STATIC_INFO static_objects[NUMBER_STATIC_OBJECTS];

sint32 *bones;
ANIM_STRUCT *anims;
RANGE_STRUCT *ranges;
CHANGE_STRUCT *changes;
sint16 **meshes, *commands, *frames;

int number_dynamics, sunset;
LIGHT_INFO dynamic[MAX_DYNAMICS];

sint32 outside;

int number_draw_rooms;
sint16 draw_rooms[100];

#ifdef PSX_VERSION
VECTOR3D CamPos, CamTgt;
#endif

/*---------------------------------------------------------------------------
 * Locals
\*--------------------------------------------------------------------------*/

static int camera_underwater;

static long cheat_active = 0;

//#ifdef PC_VERSION
static int bound_list[128];
static int bound_start=0;
static int bound_end=0;
//#endif

#if defined(GAMEDEBUG) && defined(PRINT_DRAWN_ROOMS)
static int recurse, calcdoor;
#endif

#ifdef CLIP_OUTSIDE
static int outside_left;
static int outside_right;
static int outside_top;
static int outside_bottom;
#endif

/********************************* GLOBAL VARIABLES ******************************************/


#ifdef GAMEDEBUG
void DrawRoomBox(int room_num);
void DrawBox(sint16 *bptr, int colour);
#endif


struct door_vbuf {
	sint32	xv;
	sint32	yv;
	sint32	zv;
} vbufdoor[4];

/*---------------------------------------------------------------------------
 * Local Functions
\*--------------------------------------------------------------------------*/

/****************************************************************************
* PrintObjects
*
* SYNOPSIS
*	void PrintObjects(sint16 room_number)
*
* PURPOSE
*	Draws all objects in a given room
*
* INPUTS
*	room_number	= room #
*
* RETURNS
*	Nothing
*/
static void PrintObjects(sint16 room_number)
{
	ROOM_INFO	*r;
	MESH_INFO	*mesh;
	ITEM_INFO	*item;
	sint16 i, item_num, clip;

	r = &room[room_number];

	if (r->flags & UNDERWATER)
		S_SetupBelowWater(camera_underwater);
	else
		S_SetupAboveWater(camera_underwater);

#ifdef BETTER_ROOM_SORT
#ifdef GAMEDEBUG
	if (input & IN_B)
		mid_sort = 0;
	else
		mid_sort = r->bound_active>>8;
#endif
#endif

	// Cancel active bound flag
	r->bound_active = 0;

	phd_PushMatrix();
	phd_TranslateAbs(r->x, r->y, r->z);

	// Do static mesh objects (13/8/97: made so use room clip bounds)
	phd_left = r->left;
	phd_right = r->right;
	phd_top = r->top;
	phd_bottom = r->bottom;

	mesh = r->mesh;

	for ( i=r->num_meshes ; i>0 ; i--,mesh++ )
	{
		if (static_objects[mesh->static_number].flags & 2) 		// Only Draw If Flagged as Drawable
		{
			phd_PushMatrix();
			phd_TranslateAbs(mesh->x, mesh->y, mesh->z);
			phd_RotY(mesh->y_rot);

			if ((clip = S_GetObjectBounds(&static_objects[mesh->static_number].x_minp)))
			{
				S_CalculateStaticMeshLight(mesh->x, mesh->y, mesh->z, mesh->shade, mesh->shadeB, r);
				phd_PutPolygons(meshes[static_objects[mesh->static_number].mesh_number], clip );
			}
			phd_PopMatrix();
		}
	}

// TODO: should only do this for objects that move about and so may span more than one room
	// Set bounds to full screen for objects (they'll clip badly)
	phd_left = 0;
	phd_top = 0;
	phd_right = phd_winxmax+1;
	phd_bottom = phd_winymax+1;

	// Draw items in this room
	for (item_num = r->item_number;item_num!=NO_ITEM; item_num=items[item_num].next_item )
	{
		item = &items[item_num];
		if ( item->status!=INVISIBLE )
			(*objects[item->object_number].draw_routine)(item);
	}

	// Go through linked list of effects in this room
	for ( item_num = r->fx_number;item_num!=NO_ITEM; item_num=effects[item_num].next_fx )
		DrawEffect( item_num );

	phd_PopMatrix();
	/* Reset room bounds for next time */
	r->left = phd_winxmax;
	r->top = phd_winymax;
	r->right = r->bottom = 0;
}
/****************************************************************************
* SetRoomBounds
*
* SYNOPSIS
*	void SetRoomBounds(sint16 *objptr, int room_number, ROOM_INFO *parent)
*
* PURPOSE
*	Calculates the bounds for a particular doorway
*
* INPUTS
*	objptr		= ptr to door structure
*	room_number	= target room # of door
*	parent		= source room ptr
*
* RETURNS
*	Nothing
*/
//#ifdef PC_VERSION


static void SetRoomBounds(sint16 *objptr, int room_number, ROOM_INFO *parent)
{
	int			i, z_behind, z_toofar;
	sint32		left, right, top, bottom;
	sint32		xv, yv, zv, xs, ys;
	sint32		*mptr;
	ROOM_INFO	*r;
	struct		door_vbuf	*dest, *last;

	r = &room[room_number];
#define TRY_THIS
#ifdef TRY_THIS
	/* First check if max bounds are bigger than room's current bounds; if so, nothing to calculate */
	if (r->left <= parent->test_left && r->right >= parent->test_right && r->top <= parent->test_top && r->bottom >= parent->test_bottom)
		return;
#endif

#if defined(GAMEDEBUG) && defined(PRINT_DRAWN_ROOMS)
	calcdoor++;
#endif

	/* Initialise bounds to extremes */
	left = parent->test_right;
	right = parent->test_left;
	top = parent->test_bottom;
	bottom = parent->test_top;

	objptr += 3;
	mptr = phd_mxptr;
	dest = vbufdoor;
	z_behind = z_toofar = 0;

	for ( i=0; i<4; i++, dest++, objptr+=3 )
	{
		dest->xv = xv = (sint32)( *(mptr+M00) * *(objptr) + *(mptr+M01) * *(objptr+1) + *(mptr+M02) * *(objptr+2) + *(mptr+M03) );
		dest->yv = yv = (sint32)( *(mptr+M10) * *(objptr) + *(mptr+M11) * *(objptr+1) + *(mptr+M12) * *(objptr+2) + *(mptr+M13) );
		dest->zv = zv = (sint32)( *(mptr+M20) * *(objptr) + *(mptr+M21) * *(objptr+1) + *(mptr+M22) * *(objptr+2) + *(mptr+M23) );

		/* Convert to screen space (special if -z behind you) */
		if ( zv>0 )
		{
	
			/* Keep count of number of points too far away */
			if (zv > phd_zfar)
				z_toofar++;

			zv /= phd_persp;

			if ( zv )
			{
				xs = xv/zv + phd_centerx;
				ys = yv/zv + phd_centery;
			}
			else
			{
				xs = (xv>=0) ? phd_right:phd_left;
				ys = (yv>=0) ? phd_bottom:phd_top;
			}

			/* See if change bounds */
			if ( xs-1<left )
				left = xs-1;
			if ( xs+1>right )
				right = xs+1;

			if ( ys-1<top )
				top = ys-1;
			if ( ys+1>bottom )
				bottom = ys+1;
		}
		else
			z_behind++;
	}

	if ( z_behind==4 || z_toofar==4 )	// If all points behind or too far Exit now
		return;


	/* Rough clip if doorway crosses z=0 plane */
	if (z_behind > 0)
	{
		dest = vbufdoor;
		last = dest+3;
		for (i=0; i<4; i++, last=dest, dest++)
		{
			/* Only if one point in front and one behind z=0 */
			if ( (dest->zv<0)^(last->zv<0) )
			{
				/* x-clip */
				if ( dest->xv<0 && last->xv<0 )
					left = 0;
				else if ( dest->xv>0 && last->xv>0 )
					right = phd_winxmax;
				else
				{
					left = 0;
					right = phd_winxmax;
				}

				/* y-clip */
				if ( dest->yv<0 && last->yv<0 )
					top = 0;
				else if ( dest->yv>0 && last->yv>0 )
					bottom = phd_winymax;
				else
				{
					top = 0;
					bottom = phd_winymax;
				}
			}
		}
	}

	/* Clip bounds to parent window */
	if (left < parent->test_left)
		left = parent->test_left;
	if (right > parent->test_right)
		right = parent->test_right;
	if (top < parent->test_top)
		top = parent->test_top;
	if (bottom > parent->test_bottom)
		bottom = parent->test_bottom;

	if ( left>=right || top>=bottom )
		return;

	/* Adjust this room's test bounds */
	if (r->bound_active & 2)
	{
		/* If already on list, enlarge test bounds */
		if (left < r->test_left)
			r->test_left = (sint16)left;
		if (top < r->test_top)
			r->test_top = (sint16)top;
		if (right > r->test_right)
			r->test_right = (sint16)right;
		if (bottom > r->test_bottom)
			r->test_bottom = (sint16)bottom;
	}
	else
	{
		/* Else add to bound list */
		bound_list[(bound_end++) % 128] = room_number;
		r->bound_active |= 2;
#ifdef BETTER_ROOM_SORT
		r->bound_active += (sint16)(mid_sort<<8);
#endif
		r->test_left = (sint16)left;
		r->test_right = (sint16)right;
		r->test_top = (sint16)top;
		r->test_bottom = (sint16)bottom;
	}
}
//#endif
/****************************************************************************
* GetRoomBounds
*
* SYNOPSIS
*	void GetRoomBounds()
*
* PURPOSE
*	Determines visible rooms and their clip windows
*
* INPUTS
*	None
*
* RETURNS
*	Nothing
*/
//#ifdef PC_VERSION
static void GetRoomBounds(void)
{
	ROOM_INFO	*r;
	sint16 *door;
	int i, room_number;
	int a;

	while (bound_start != bound_end)
	{
		room_number = bound_list[(bound_start++) % 128];
		r = &room[room_number];
		r->bound_active -= 2;
#ifdef BETTER_ROOM_SORT
		mid_sort = (r->bound_active >> 8) + 1;
#endif
		/* Enlarge room bounds to incorporate test bounds */
		if (r->test_left < r->left)
			r->left = r->test_left;
		if (r->test_top < r->top)
			r->top = r->test_top;
		if (r->test_right > r->right)
			r->right = r->test_right;
		if (r->test_bottom > r->bottom)
			r->bottom = r->test_bottom;

		/* Add to draw list if not already on it */
		if (!(r->bound_active & 1))
		{
			draw_rooms[number_draw_rooms++] = room_number;
			r->bound_active |= 1;

			/* Set outside flag if this room can see it */
			if (r->flags & OUTSIDE)
				outside = OUTSIDE;
		}

#ifdef CLIP_OUTSIDE
		/* Enlarge outside polygon draw area using room bounds */
#ifdef PSX_VERSION
		if (r->flags & OUTSIDE)
#else
		if (!(r->flags & INSIDE))
#endif
		{
			if (r->left < outside_left)
				outside_left = r->left;
			if (r->right > outside_right)
				outside_right = r->right;
			if (r->top < outside_top)
				outside_top = r->top;
			if (r->bottom > outside_bottom)
				outside_bottom = r->bottom;
		}
#endif

		phd_PushMatrix();
		phd_TranslateAbs(r->x, r->y, r->z);

		if ( (door = r->door) )
		{
			for (i=*(door++); i>0; i--, door+=15)
			{
				room_number = *(door++);
#if defined(GAMEDEBUG) && defined(PRINT_DRAWN_ROOMS)
	recurse++;
#endif

				/* Use door normal to decide whether to continue checking this doorway */
/*				if ( (sint32)( *(door+0) * (r->x + *(door+3) - *(w2v_matrix+M03) ) )
					+ (sint32)( *(door+1) * (r->y + *(door+4) - *(w2v_matrix+M13) ) )
					+ (sint32)( *(door+2) * (r->z + *(door+5) - *(w2v_matrix+M23) ) )
					>=0 )
					continue;

				SetRoomBounds(door, room_number, r);
*/

			if(*(door+0) != 0)
			{
				a = (r->x + *(door+3) - *(w2v_matrix+M03));			
				if(a == 0) continue;
				if((a^*(door+0))<0) 
					SetRoomBounds(door, room_number, r);
				continue;
			}

			if(*(door+1) != 0) 
			{
				a = (r->y + *(door+4) - *(w2v_matrix+M13));			
				if(a == 0) continue;
				if((a^*(door+1))<0) 
					SetRoomBounds(door, room_number, r);
				continue;
			}

			if(*(door+2) != 0)
			{
				a = (r->z + *(door+5) - *(w2v_matrix+M23));			
				if(a == 0) continue;
				if((a^*(door+2))<0) 
					SetRoomBounds(door, room_number, r);
			}

	
	
			}
		}
		phd_PopMatrix();
	}
}
//#endif

/*---------------------------------------------------------------------------
 * Global Functions
\*--------------------------------------------------------------------------*/

/******************************************************************************
 *			Draw Phase For Cinematics No health bars etc
 *****************************************************************************/
sint32 DrawPhaseCinematic(void)
{
//	int			i;
//	ROOM_INFO	*r;

#ifndef PC_VERSION
	S_InitialisePolyList();
	S_ClearScreen();
#endif

	camera_underwater = 0;                    // No underwater cameras

	DrawRooms(camera.pos.room_number);
//	for ( i=0; i<number_draw_rooms; i++ )
//	{
//		DrawRoomBox(draw_rooms[i]);
//	}
//	DrawRooms(lara_item->room_number);
/*	for ( i=0; i<number_draw_rooms; i++ )
	{
		r = &room[ draw_rooms[i] ];
		r->left = r->top = 0;
		r->right = phd_winxmax;
		r->bottom = phd_winymax;
		PrintRooms( draw_rooms[i] );
	}
*/
#if defined(GAMEDEBUG) || defined(SHOW_ROOM_INFO)
	T_DrawText();
#endif
	S_OutputPolyList();
//#ifdef PSX_VERSION
//	camera.number_frames = S_DumpScreenCine();
//#else
	camera.number_frames = S_DumpScreen();
//#endif
#ifdef SAVE_CINI
	camera.number_frames = 2;
	S_SavePicture();
#endif
	S_AnimateTextures(camera.number_frames);
	return (camera.number_frames);
}

/******************************************************************************
 *			Draw Phase for the Game  includes Information Draw...
 *****************************************************************************/
sint32 DrawPhaseGame(void)
{

#ifndef PC_VERSION
	S_InitialisePolyList();
#endif

#ifdef PSX_VERSION
	DrawGameInfo(1);
	DrawRooms(camera.pos.room_number);
#else
	DrawRooms(camera.pos.room_number);



	DrawGameInfo(1);
#endif
	S_OutputPolyList();

	camera.number_frames = S_DumpScreen();
	S_AnimateTextures(camera.number_frames);

	return (camera.number_frames);
}

/*****************************************************************************
 *		Add All Visible Rooms (and contents) from current_room to Draw List
 ****************************************************************************/

void DrawRooms(sint16 current_room)
{
	int 		i;
	ROOM_INFO	*r;

	/* Set bounds of first room and initialise clear space */
	r = &room[current_room];
	phd_left = r->test_left = 0;
	phd_top = r->test_top = 0;
	phd_right = r->test_right = phd_winxmax;
	phd_bottom = r->test_bottom = phd_winymax;

#if 0
	phd_left+=64; phd_right-=64; phd_top+=48; phd_bottom-=48;
	r->test_left+=64; r->test_right-=64; r->test_top+=48; r->test_bottom-=48;
	S_DrawScreenBox(64,48,0,256,144,5,0,1);
	S_DrawScreenFBox(65,49,0,254,142,0,0,1);
#endif

	outside = r->flags & OUTSIDE;
	camera_underwater = r->flags & UNDERWATER;

/* -------- initialise bound vars */

//##ifdef PC_VERSION
	r->bound_active = 2;

	bound_list[0] = current_room;
	bound_start = 0;
	bound_end = 1;

	number_draw_rooms = 0;
//##endif

/* -------- initialise horizon clipping */

#ifdef CLIP_OUTSIDE
	if (outside)
	{
		outside_left = outside_top = 0;
		outside_right = phd_winxmax;
		outside_bottom = phd_winymax;
	}
	else
	{
		outside_left = phd_winxmax;
		outside_top = phd_winymax;
		outside_right = outside_bottom = 0;
	}
#endif

/* -------- recursively set up which rooms should be drawn */

#if defined(GAMEDEBUG) && defined(PRINT_DRAWN_ROOMS)
	calcdoor = recurse = 0;
#endif

#ifdef PSX_VERSION
	if (input & IN_SLOW)
	{
		CamPos.x = camera.pos.x;
		CamPos.y = camera.pos.y + camera.shift;
		CamPos.z = camera.pos.z;

		CamTgt.x = camera.target.x;
		CamTgt.y = camera.target.y;
		CamTgt.z = camera.target.z;

		mInitW2VMatrix(&CamPos, &CamTgt);

		GetRoomBoundsAsm(current_room);
	}
	else
		GetRoomBounds();
#else
	GetRoomBounds();
#endif

#ifdef BETTER_ROOM_SORT
	mid_sort = 0;
#endif

	/* Only clear screen if outside flag has been set */
	if (outside)
	{

#ifdef CLIP_OUTSIDE
		phd_left = outside_left;
		phd_right = outside_right;
		phd_top = outside_top;
		phd_bottom = outside_bottom;
#endif
		if (objects[HORIZON].loaded)
		{
			sint16 *frame;

			S_SetupAboveWater(camera_underwater);

			phd_PushMatrix();

			// translates need to be v.small as HORIZON is really an object about 4 blocks radius
//			phd_TranslateRel(0,WALL_L,0);
			*(phd_mxptr+M03) = *(phd_mxptr+M13) = *(phd_mxptr+M23) = 0;

			frame = anims[objects[HORIZON].anim_index].frame_ptr + 9;
			gar_RotYXZsuperpack(&frame, 0);

#ifdef PC_VERSION
			S_InitialisePolyList(SIPL_DONT_CLEAR_SCREEN);
#endif
			S_InsertBackground(meshes[objects[HORIZON].mesh_index]);
			phd_PopMatrix();
		}
		else
		{
			// no horizon sphere, so better clear screen instead; set outside to -1 as don't need back polys
#ifdef PC_VERSION
			S_InitialisePolyList(SIPL_CLEAR_SCREEN);
#else
			S_ClearScreen();
#endif
			outside = -1;
		}
	}
#ifdef PC_VERSION
	else
		S_InitialisePolyList(SIPL_DONT_CLEAR_SCREEN);		// don't clear screen
#endif



	// Draw Lara first so she doesnt disappear due to Polygon limiter
	if ( objects[LARA].loaded && !(lara_item->flags&ONESHOT))
	{
		if ( room[lara_item->room_number].flags&UNDERWATER )
			S_SetupBelowWater(camera_underwater);
		else
			S_SetupAboveWater(camera_underwater);
#ifdef BETTER_ROOM_SORT
			mid_sort = room[lara_item->room_number].bound_active>>8;
			if (mid_sort)
				mid_sort--;
#endif
			DrawLara( lara_item );
	}

#if defined(GAMEDEBUG) && defined(PRINT_DRAWN_ROOMS)
	sprintf(exit_message, "Rooms:%d Recurse:%d Door:%d",number_draw_rooms,recurse,calcdoor);
	PrintDbug(4,5,exit_message);
#endif

	// Now Insert all rooms starting at nearest
#ifdef PC_VERSION
	for ( i=0;i<number_draw_rooms;i++ )
		PrintRooms( draw_rooms[i] );

#else

	{
		LONG waterclut;

		if (input & IN_LOOK && !cheat_active)
		{
			printf("Cheating!\n");
			cheat_active = 1;

			Inv_AddItem(HARPOON_ITEM);
 			Inv_AddItem(ROCKET_GUN_ITEM);
 			Inv_AddItem(M16_ITEM);
 			Inv_AddItem(SHOTGUN_ITEM);
 			Inv_AddItem(MAGNUM_ITEM);
 			Inv_AddItem(UZI_ITEM);

// 			lara.shotgun.ammo = 500;
// 			lara.magnums.ammo = 500;
// 			lara.uzis.ammo = 5000;
 			lara.rocket.ammo = 5000;
// 			lara.m16.ammo = 5000;
// 			lara.harpoon.ammo = 5000;
/* 			for (i=0; i<50; i++)
 			{
 				Inv_AddItem(MEDI_ITEM);
				Inv_AddItem(BIGMEDI_ITEM);
				Inv_AddItem(FLARE_ITEM);
			}*/
		}

		CamPos.x = camera.pos.x;
		CamPos.y = camera.pos.y + camera.shift;
		CamPos.z = camera.pos.z;

		CamTgt.x = camera.target.x;
		CamTgt.y = camera.target.y;
		CamTgt.z = camera.target.z;

		mInitW2VMatrix(&CamPos, &CamTgt);

		waterclut = (camera_underwater) ? GdwUWClutSub : 0;

		DrawRoomletListAsm(waterclut);

		S_DrawSparks();
	}
#endif

#ifndef OLD_PRINT_ROOMS
	// Now insert all objects
	for ( i=0;i<number_draw_rooms;i++ )
		PrintObjects( draw_rooms[i] );
#endif

//#ifdef PC_VERSION
//	S_DrawSparks();
//#endif

}


/******************************************************************************
 *					Print A Given Room please
 *****************************************************************************/

/* Define the lines that make a box from 8 points */
int box_lines[12][2] = {
	{0, 1}, {1, 2}, {2, 3}, {3, 0},
	{4, 5}, {5, 6}, {6, 7}, {7, 4},
	{0, 4}, {1, 5}, {2, 6}, {3, 7}
};

#ifdef PSX_VERSION
void ClipRoom(ROOM_INFO *r,int nTX,int nTY,int nTZ)
#else
void ClipRoom(ROOM_INFO *r)
#endif
{
//#ifdef PC_VERSION
	/* Define a box enclosing the room, then clip it against the far plane to
		get the dimensions of an area that needs clearing to the depthQ colour */
	int i, p1, p2, clip_room;
	sint32 xv[8], yv[8], zv[8], clip[8];
	sint32 x, y, z, *mptr;
	sint32 min_x, min_y, max_x, max_y;
	sint32 zdiv, znom;

// TODO: should also use this to determine bounds of sky region (so not whole screen)
// i.e. use overall bounds of room as size of sky box and see if this increases size of sky box needed so far

	/* Fill in the coordinates of the room */
	xv[0] = xv[3] = xv[4] = xv[7] = WALL_L;							// min x
	yv[0] = yv[1] = yv[2] = yv[3] = r->maxceiling - r->y;
	zv[0] = zv[1] = zv[4] = zv[5] = WALL_L;							// min z

	xv[1] = xv[2] = xv[5] = xv[6] = (r->y_size-1) * WALL_L;		// max x
	yv[4] = yv[5] = yv[6] = yv[7] = r->minfloor - r->y;				// max y
	zv[2] = zv[3] = zv[6] = zv[7] = (r->x_size-1) * WALL_L;		// max z

	mptr = phd_mxptr;

	/* Convert coords to viewspace */
	clip_room = 0;
	for (i=0; i<8; i++)
	{
#ifdef PSX_VERSION
		x = xv[i]+nTX;
		y = yv[i]+nTY;
		z = zv[i]+nTZ;
#else
		x = xv[i];
		y = yv[i];
		z = zv[i];
#endif

		xv[i] = (*(mptr+M00) * x) + (*(mptr+M01) * y) + (*(mptr+M02) * z) + *(mptr+M03);
		yv[i] = (*(mptr+M10) * x) + (*(mptr+M11) * y) + (*(mptr+M12) * z) + *(mptr+M13);
		zv[i] = (*(mptr+M20) * x) + (*(mptr+M21) * y) + (*(mptr+M22) * z) + *(mptr+M23);

		if (zv[i] > phd_zfar)
			clip[i] = clip_room = 1;
		else
			clip[i] = 0;
	}

	/* Not one point is beyond far plane, so no more testing required */
	if (!clip_room)
		return;

	/* Clip all the lines that make up the box against the far-plane */
	min_x = min_y = 0x10000000;
	max_x = max_y = -0x10000000;
	for (i=0; i<12; i++)
	{
		p1 = box_lines[i][0];
		p2 = box_lines[i][1];

		/* Only needs clipping if 1 of the 2 points is crosses line */
		if (clip[p1] ^ clip[p2])
		{
			/* Get clipping point (have to avoid overflow ny shifting about) */
			zdiv = (zv[p2] - zv[p1]) >> W2V_SHIFT;
			if (zdiv)
			{
#ifdef PSX_VERSION
				znom = ((phd_zfar - zv[p1]) >> W2V_SHIFT) - WALL_L;
#else
				znom = (phd_zfar - zv[p1]) >> W2V_SHIFT;
#endif
				x = xv[p1] + ((((xv[p2] - xv[p1]) >> W2V_SHIFT) * znom / zdiv) << W2V_SHIFT);
				y = yv[p1] + ((((yv[p2] - yv[p1]) >> W2V_SHIFT) * znom / zdiv) << W2V_SHIFT);

				/* Adjust bounds of depthQ polygon */
				if (x < min_x)
					min_x = x;
				if (x > max_x)
					max_x = x;

				if (y < min_y)
					min_y = y;
				if (y > max_y)
					max_y = y;
			}
			else
			{
				/* In case of divide by zero situations */
				if (xv[p1] < min_x)
					min_x = xv[p1];
				if (xv[p2] < min_x)
					min_x = xv[p2];
				if (xv[p1] > max_x)
					max_x = xv[p1];
				if (xv[p2] > max_x)
					max_x = xv[p2];

				if (yv[p1] < min_y)
					min_y = yv[p1];
				if (yv[p2] < min_y)
					min_y = yv[p2];
				if (yv[p1] > max_y)
					max_y = yv[p1];
				if (yv[p2] > max_y)
					max_y = yv[p2];
			}
		}
	}

	/* Convert coords of depthQ polygon into screen coords */
#ifdef PSX_VERSION
	z = (phd_zfar-(WALL_L<<W2V_SHIFT))/phd_persp;
#else
	z = phd_zfar/phd_persp;
#endif
	min_x = min_x/z + phd_centerx;
	min_y = min_y/z + phd_centery;
	max_x = max_x/z + phd_centerx;
	max_y = max_y/z + phd_centery;

	/* If it's off the screen anyway, then forget it */
	if ( (min_x > phd_right) || (min_y > phd_bottom) || (max_x < phd_left) || (max_y < phd_top) )
		return;

	if (min_x < phd_left)
		min_x = phd_left;
	if (min_y < phd_top)
		min_y = phd_top;
	if (max_x > phd_right)
		max_x = phd_right;
	if (max_y > phd_bottom)
		max_y = phd_bottom;

	/* Place this polygon onto the draw list */
	S_InsertBackPolygon(min_x, min_y, max_x, max_y, 0x00000000);
//#endif
}

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

int CurrentRoom;

void	PrintRooms(sint16 room_number)
{
	ROOM_INFO *r;
#ifdef PSX_VERSION
	ROOM_INFO *cr;
	int			nTX,nTY,nTZ,nMX,nMZ,nTXY;
#endif

	CurrentRoom = room_number;

	r = &room[room_number];
	if ( r->flags&UNDERWATER )
		S_SetupBelowWater(camera_underwater);
	else
		S_SetupAboveWater(camera_underwater);

#ifdef BETTER_ROOM_SORT
#ifdef GAMEDEBUG
	if (input & IN_B)
		mid_sort = 0;
	else
		mid_sort = r->bound_active>>8;
#endif
#endif

#ifdef PSX_VERSION	// Dude's awesome anti-crack frig
	cr=room+camera.pos.room_number;				// cr=ptr to current room (where camera is... middle of scene)
	nTX=r->x-cr->x;
	nTY=r->y-cr->y;								// calc translation to this room
	nTZ=r->z-cr->z;
	nMX=nTX+(r->y_size<<WALL_SHIFT);			// calc max world x/z for room
	nMZ=nTZ+(r->x_size<<WALL_SHIFT);			// note room z max is called 'x_size'... and room x max is called 'y_size'...
	if ((nTX>=-0x8000) && (nMX<0x8000) && (nTZ>=-0x8000) && (nMZ<0x8000))	// see if this room can be drawn with vertex offset
		{
		nTXY=((nTY+(nTX>>31))<<16)|(nTX&0xffff);	// it can, so calc packed vertex offset (adjust Y for X overflow if necessary)
		phd_TranslateAbs(cr->x, cr->y, cr->z);	// use common translation, ie camera room, and nTX/nTY/nTZ are vertex offsets
		}
	else
		{
		nTXY=nTZ=0;								// it can't, so give up... no vertex offset
		phd_TranslateAbs(r->x, r->y, r->z);		// translate matrix by actual room pos
		}
#else
	phd_TranslateAbs(r->x, r->y, r->z);
#endif

	// Set bounds and put this room's mesh on list
	phd_left = r->left;
	phd_right = r->right;
	phd_top = r->top;
	phd_bottom = r->bottom;

//	S_LightRoom(r);
	if (outside>0 && !(r->flags & INSIDE))
	{
#ifdef PSX_VERSION
//		ClipRoom(r,nTX,nTY,nTZ);				// draw back poly in rooms adjacent to sky rooms
//##		S_InsertRoom(r->data, 1, nTXY, nTZ);	// send vertex offset (might be 0,0,0)
#else
		S_InsertRoom(r->data, 1);				// draw right to depthQ
#endif
	}
	else
	{
#ifdef PSX_VERSION
		if (outside>=0)
			ClipRoom(r,nTX,nTY,nTZ);			// draw back poly (give ClipRoom the vertex offset vector...)
//##		S_InsertRoom(r->data, 0, nTXY, nTZ);
#else
		if (outside>=0)
			ClipRoom(r);						// draw back poly
		S_InsertRoom(r->data, 0);				// stop at depthQ
#endif
	}
#ifdef PSX_VERSION
	S_RestoreLightRoom(r);
#endif
}




/*****************************************************************************
 *			Draw An Effect Sprite onto Screen....
 ****************************************************************************/
void	DrawEffect( sint16 fx_num )
{
	FX_INFO 	*fx;
	OBJECT_INFO *object;

	fx = &effects[fx_num];

	if (fx->object_number == FLAME)	// Don't draw flames 'cos they're shite.
		return;

	object = &objects[fx->object_number];

	if (!object->loaded)
	{
#ifdef GAMEDEBUG
		wsprintf( exit_message,"Object not Loaded:%d\n",fx->object_number );
#endif
		return;
	}

	if (fx->object_number==GLOW)
		{
		S_DrawSprite(((uint16)fx->pos.x_rot)|(fx->pos.y_rot<<16),	// flags
					 fx->pos.x_pos,fx->pos.y_pos,fx->pos.z_pos,		// pos
					 objects[GLOW].mesh_index,						// sprite number
					 fx->shade,										// shade
					 fx->frame_number);								// scale
		return;
		}

	/* NOTE: zero nmeshes means this effect had a mesh selected on creation: fx->frame_number stores 'mesh_number' */
	if (object->nmeshes < 0)
	{
		S_DrawSprite(SPRITE_ABS|(object->semi_transparent?SPRITE_SEMITRANS:0)|SPRITE_SHADE,fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, (sint16)(object->mesh_index - fx->frame_number), fx->shade, 0);
	}
	else
	{
		phd_PushMatrix();
		phd_TranslateAbs(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos);
		if ( *(phd_mxptr+M23)>phd_znear && *(phd_mxptr+M23)<phd_zfar )
		{
			phd_RotYXZ(fx->pos.y_rot, fx->pos.x_rot, fx->pos.z_rot);
			if (object->nmeshes)
			{
				S_CalculateStaticLight(fx->shade);
				phd_PutPolygons(meshes[object->mesh_index], -1 );
			}
			else
			{
				//S_CalculateLight(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, fx->room_number);
				S_CalculateStaticLight(fx->shade);
				phd_PutPolygons(meshes[fx->frame_number], -1 );
			}
		}
		phd_PopMatrix();
	}
}

/*****************************************************************************
 *				Draw Item As a Sprite
 ****************************************************************************/
void	DrawSpriteItem( ITEM_INFO *item )
{
	OBJECT_INFO	*obj;

	S_CalculateStaticMeshLight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->shade, item->shadeB, &room[item->room_number]);

	obj = &objects[item->object_number];
	S_DrawSprite(SPRITE_ABS|(obj->semi_transparent?SPRITE_SEMITRANS:0)|SPRITE_SHADE,
				 item->pos.x_pos,item->pos.y_pos,item->pos.z_pos,
				 (sint16)(obj->mesh_index - item->frame_number),
				 (sint16)(ls_adder+16*256),0);
}

/****************************************************************************
 *		Dummy Draw Routine for NonVisible objects Cameras,Sounds etc
 ***************************************************************************/
void	DrawDummyItem( ITEM_INFO *item )
{
}

/****************************************************************************
 *						Draw Animating Item
 ***************************************************************************/
	sint16		null_rotations[] = { 0,0,0,0,0,0,
									 0,0,0,0,0,0 };

void	DrawAnimatingItem( ITEM_INFO *item )
{
	int 		clip,i,poppush;
	OBJECT_INFO *object;
	sint32 		*bone;
	sint16		*rotation1, *rotation2;
	sint16		*extra_rotation;
	sint16		**meshpp;
	uint32 		bit;
	sint16		*frmptr[2];
	int			rate,frac;
//	char		str[32];

	frac=GetFrames( item,frmptr,&rate );

	object = &objects[item->object_number];					// Draw Shadows...
	if (object->shadow_size)
		S_PrintShadow( object->shadow_size,frmptr[0],item,0 );

	phd_PushMatrix();
	phd_TranslateAbs(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);

	if ( (clip=S_GetObjectBounds(frmptr[0])) )
	{
		CalculateObjectLighting( item,frmptr[0] );

		if ( item->data )
			extra_rotation = (sint16 *)item->data;			// Extra rotations
		else
			extra_rotation = null_rotations;				// Extra rotations


		meshpp = &meshes[object->mesh_index];           	// Mesh Pointers
		bone = bones + object->bone_index;              	// Bone pointer
		bit = 1;                            				// Used for Mesh On/Off Test

		if ( !frac )
		{
			phd_TranslateRel((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8));
			rotation1 = frmptr[0] + 9;
			gar_RotYXZsuperpack(&rotation1, 0);

			if (bit & item->mesh_bits)
				phd_PutPolygons( *meshpp, clip );
			meshpp++;

			for ( i=object->nmeshes-1; i>0; i--,bone+=4,meshpp++ )
			{
				poppush = *(bone);							// Do Automated Hierarchy
				if (poppush & 1)                			// Push Pop
					phd_PopMatrix();
				if (poppush & 2)
					phd_PushMatrix();

				phd_TranslateRel( *(bone+1), *(bone+2), *(bone+3) );
				gar_RotYXZsuperpack(&rotation1, 0);

				if (poppush & (ROT_X|ROT_Y|ROT_Z))			// If any Extra rotations required...
				{
					if (poppush & ROT_Y)
						phd_RotY(*(extra_rotation++));
					if (poppush & ROT_X)
						phd_RotX(*(extra_rotation++));
					if (poppush & ROT_Z)
						phd_RotZ(*(extra_rotation++));
				}

				bit <<= 1;
				if (bit & item->mesh_bits)
					phd_PutPolygons( *meshpp, clip );
			}
		}
		else
		{
			InitInterpolate( frac,rate );
			phd_TranslateRel_ID( (sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8),
								 (sint32)*(frmptr[1]+6), (sint32)*(frmptr[1]+7), (sint32)*(frmptr[1]+8) );
			rotation1 = frmptr[0]+9;
			rotation2 = frmptr[1]+9;
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

			if (bit & item->mesh_bits)
				phd_PutPolygons_I( *meshpp, clip );
			meshpp++;

			for ( i=object->nmeshes-1; i>0; i--,bone+=4,meshpp++ )
			{
				poppush = *(bone);						// Do Automated Hierarchy
				if (poppush & 1)                			// Push Pop
					phd_PopMatrix_I();
				if (poppush & 2)
					phd_PushMatrix_I();

				phd_TranslateRel_I(*(bone+1), *(bone+2), *(bone+3));
				gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

				if (poppush & (ROT_X|ROT_Y|ROT_Z))			// If any Extra rotations required...
				{
					if (poppush & ROT_Y)
						phd_RotY_I(*(extra_rotation++));
					if (poppush & ROT_X)
						phd_RotX_I(*(extra_rotation++));
					if (poppush & ROT_Z)
						phd_RotZ_I(*(extra_rotation++));
				}

				bit <<= 1;
				if (bit & item->mesh_bits)
					phd_PutPolygons_I( *meshpp, clip );
			}

		}
	}
	phd_PopMatrix();
}


/*****************************************************************************
 *				Draw Lara special routines....
 ****************************************************************************/
#ifdef PSX_VERSION
#define PutPolyLara( A,B )		emap_flag = lara.mesh_effects&(1<<A);\
								phd_PutPolygons( lara.mesh_ptrs[A],B );\
								emap_flag = 0
#define PutPolyLara_I( A,B )	emap_flag = lara.mesh_effects&(1<<A);\
								phd_PutPolygons_I( lara.mesh_ptrs[A],B );\
								emap_flag = 0
#else
#define PutPolyLara( A,B )		phd_PutPolygons( lara.mesh_ptrs[A],B )
#define PutPolyLara_I( A,B )    phd_PutPolygons_I( lara.mesh_ptrs[A],B )
#endif

void	DrawLara( ITEM_INFO *item )
{
	OBJECT_INFO *object;
	sint32 		*bone, *bonew;
	sint16		*rotation, *rotationw;
	sint16 		*frame;
	int			clip,fire_arms;
	sint32		savemox[12];
	sint16		*frmptr[2];
	int			frac,rate,size;
	int			left,right,top,bottom;
	long			do_flare=0;

	left = phd_left;
	right = phd_right;
	top = phd_top;
	bottom = phd_bottom;
	phd_left = phd_top = 0;							// Make Bounds to Screen Limits
	phd_bottom = phd_winymax;
	phd_right = phd_winxmax;

#ifdef PSX_VERSION
	emap_tint=0x6098a0;		// nice gold colour
#endif

	if ( lara.hit_direction<0 && (frac=GetFrames(item,frmptr,&rate) ) )
	{
		DrawLaraInt( item,frmptr[0],frmptr[1],frac,rate );
		phd_left = left;                    		// Reset bounds to what they were
		phd_right = right;
		phd_top = top;
		phd_bottom = bottom;
		return;
	}
	object = &objects[item->object_number];

	if ( lara.hit_direction>=0 )
	{
		switch ( lara.hit_direction )
		{
			default:
			case NORTH:     frame = anims[SPAZ_FORWARD_A].frame_ptr; size = anims[SPAZ_FORWARD_A].interpolation>>8; break;
			case EAST:		frame = anims[SPAZ_RIGHT_A].frame_ptr;	size = anims[SPAZ_RIGHT_A].interpolation>>8; break;
			case SOUTH:		frame = anims[SPAZ_BACK_A].frame_ptr; size = anims[SPAZ_BACK_A].interpolation>>8; break;
			case WEST:		frame = anims[SPAZ_LEFT_A].frame_ptr; size = anims[SPAZ_LEFT_A].interpolation>>8; break;
		}
		frame += (int)( lara.hit_frame*size );
	}
	else
		frame = frmptr[0];


	if (lara.skidoo == NO_ITEM)
		S_PrintShadow( object->shadow_size,frame,item,0 );		// Lara always has a shadow..

	// HACK: save standard matrix for hair
	S_LongMemCpy( savemox,phd_mxptr,12 );

	phd_PushMatrix();
	phd_TranslateAbs(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);
//	DrawBox(frame, C_WHITE);
	if (  !(clip=S_GetObjectBounds(frame)) )
	{
		phd_PopMatrix();
		return;
	}
	phd_PushMatrix();

	CalculateObjectLightingLara();

	bone = bones + object->bone_index;
	rotation = frame+9;

	phd_TranslateRel((sint32)*(frame+6), (sint32)*(frame+7), (sint32)*(frame+8));
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( HIPS,clip );

	phd_PushMatrix();
	phd_TranslateRel( *(bone+1), *(bone+2), *(bone+3) );
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( THIGH_L, clip );

	phd_TranslateRel( *(bone+1+4), *(bone+2+4), *(bone+3+4) );
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( CALF_L, clip );

	phd_TranslateRel( *(bone+1+8), *(bone+2+8), *(bone+3+8) );
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( FOOT_L, clip );

	phd_PopMatrix();
	phd_PushMatrix();
	phd_TranslateRel( *(bone+1+12), *(bone+2+12), *(bone+3+12) );
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( THIGH_R, clip );

	phd_TranslateRel( *(bone+1+16), *(bone+2+16), *(bone+3+16) );
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( CALF_R, clip );

	phd_TranslateRel( *(bone+1+20), *(bone+2+20), *(bone+3+20) );
	gar_RotYXZsuperpack(&rotation, 0);
	PutPolyLara( FOOT_R, clip );

	phd_PopMatrix();				// Torso..
	phd_TranslateRel( *(bone+1+24), *(bone+2+24), *(bone+3+24) );

	if (lara.weapon_item!=NO_ITEM && lara.gun_type==LG_M16 &&
		 (items[lara.weapon_item].current_anim_state==0 || items[lara.weapon_item].current_anim_state==2 || items[lara.weapon_item].current_anim_state==4))
	{
		rotation = lara.right_arm.frame_base + lara.right_arm.frame_number * (anims[lara.right_arm.anim_number].interpolation>>8) + 9;
		gar_RotYXZsuperpack(&rotation, 7);
	}
	else
		gar_RotYXZsuperpack(&rotation, 0);
	phd_RotYXZ( lara.torso_y_rot, lara.torso_x_rot, lara.torso_z_rot );
	PutPolyLara( TORSO, clip );

	phd_PushMatrix();               // Head
	phd_TranslateRel( *(bone+1+52), *(bone+2+52), *(bone+3+52) );
	rotationw = rotation;
	gar_RotYXZsuperpack(&rotation, 6);
	rotation = rotationw;
	phd_RotYXZ( lara.head_y_rot,lara.head_x_rot,lara.head_z_rot );
	PutPolyLara( HEAD, clip );

	// HACK: Draw hair here as we want to use Lara's lighting, so needs to go before gun flashes and this is a convenient spot
	S_LongMemCpy( phd_mxptr,savemox,12 );
	DrawHair();

	phd_PopMatrix();

	if (lara.back_gun)
	{
		// lara.back_gun is the object number of the gun that is on Lara's back at present (if any)
		phd_PushMatrix();
		bonew = bones + objects[lara.back_gun].bone_index;
		phd_TranslateRel( *(bonew+1+52), *(bonew+2+52), *(bonew+3+52) );
		rotationw = objects[lara.back_gun].frame_base + 9;
		gar_RotYXZsuperpack(&rotationw, 14);
		phd_PutPolygons(meshes[objects[lara.back_gun].mesh_index + HEAD], clip);
		phd_PopMatrix();
	}

	fire_arms = LG_UNARMED;						// Get what Gun is currently in Use
	if ( lara.gun_status==LG_READY ||       	// if Guns are Away dont use Animations...
		lara.gun_status==LG_SPECIAL ||
		lara.gun_status==LG_DRAW ||
		lara.gun_status==LG_UNDRAW )
		 	fire_arms = lara.gun_type;

	switch ( fire_arms )
	{
		case LG_UNARMED:					// UNARMED
		case LG_FLARE:
			phd_PushMatrix();
			phd_TranslateRel( *(bone+1+28),*(bone+2+28),*(bone+3+28) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( UARM_R, clip );					// Right Arm insert
			phd_TranslateRel( *(bone+1+32), *(bone+2+32), *(bone+3+32) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( LARM_R, clip );
			phd_TranslateRel( *(bone+1+36), *(bone+2+36), *(bone+3+36) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( HAND_R, clip );
			phd_PopMatrix();

			phd_PushMatrix();
			phd_TranslateRel( *(bone+1+40), *(bone+2+40), *(bone+3+40) );

			if (lara.flare_control_left)
			{
				rotation = lara.left_arm.frame_base + (lara.left_arm.frame_number - anims[lara.left_arm.anim_number].frame_base)
																	* (anims[lara.left_arm.anim_number].interpolation>>8) + 9;
				gar_RotYXZsuperpack(&rotation, 11);
			}
			else
				gar_RotYXZsuperpack(&rotation, 0);

			PutPolyLara( UARM_L, clip );
			phd_TranslateRel( *(bone+1+44), *(bone+2+44), *(bone+3+44) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( LARM_L, clip );
			phd_TranslateRel( *(bone+1+48), *(bone+2+48), *(bone+3+48) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( HAND_L, clip );

			// plonk the flare burn on left hand
			if (lara.gun_type==LG_FLARE && lara.left_arm.flash_gun)
			{
				do_flare=1;
				DrawGunFlash( LG_FLARE, clip );
			}

			break;

		case LG_PISTOLS:
		case LG_MAGNUMS:
		case LG_UZIS:
			phd_PushMatrix();
			phd_TranslateRel( *(bone+1+28), *(bone+2+28), *(bone+3+28) );
			*(phd_mxptr+M00) = *(phd_mxptr+M00-24);
			*(phd_mxptr+M01) = *(phd_mxptr+M01-24);	  	// Restore Laras Main Rotations
			*(phd_mxptr+M02) = *(phd_mxptr+M02-24);     // Less any torso rotations
			*(phd_mxptr+M10) = *(phd_mxptr+M10-24);     // avoids Wobbly arms when
			*(phd_mxptr+M11) = *(phd_mxptr+M11-24);     // moving 'n' shooting
			*(phd_mxptr+M12) = *(phd_mxptr+M12-24);
			*(phd_mxptr+M20) = *(phd_mxptr+M20-24);
			*(phd_mxptr+M21) = *(phd_mxptr+M21-24);
			*(phd_mxptr+M22) = *(phd_mxptr+M22-24);
			phd_RotYXZ( lara.right_arm.y_rot, lara.right_arm.x_rot, lara.right_arm.z_rot );
			rotation = lara.right_arm.frame_base + (lara.right_arm.frame_number - anims[lara.right_arm.anim_number].frame_base)
																	* (anims[lara.right_arm.anim_number].interpolation>>8) + 9;
			gar_RotYXZsuperpack(&rotation, 8);
			PutPolyLara( UARM_R, clip );
			phd_TranslateRel( *(bone+1+32), *(bone+2+32), *(bone+3+32) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( LARM_R, clip );
			phd_TranslateRel( *(bone+1+36), *(bone+2+36), *(bone+3+36) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( HAND_R, clip );
			if ( lara.right_arm.flash_gun )
				S_LongMemCpy( savemox,phd_mxptr,12 );	// If Firing Gun save where Gun Flash is....

			phd_PopMatrix();
			phd_PushMatrix();
			phd_TranslateRel( *(bone+1+40), *(bone+2+40), *(bone+3+40) );
			*(phd_mxptr+M00) = *(phd_mxptr+M00-24);
			*(phd_mxptr+M01) = *(phd_mxptr+M01-24);	  	// Restore Laras Main Rotations
			*(phd_mxptr+M02) = *(phd_mxptr+M02-24);     // Less any torso rotations
			*(phd_mxptr+M10) = *(phd_mxptr+M10-24);     // avoids Wobbly arms when
			*(phd_mxptr+M11) = *(phd_mxptr+M11-24);     // moving 'n' shooting COOL
			*(phd_mxptr+M12) = *(phd_mxptr+M12-24);
			*(phd_mxptr+M20) = *(phd_mxptr+M20-24);
			*(phd_mxptr+M21) = *(phd_mxptr+M21-24);
			*(phd_mxptr+M22) = *(phd_mxptr+M22-24);
			phd_RotYXZ( lara.left_arm.y_rot, lara.left_arm.x_rot, lara.left_arm.z_rot );
			rotation = lara.left_arm.frame_base + (lara.left_arm.frame_number - anims[lara.left_arm.anim_number].frame_base)
																	* (anims[lara.left_arm.anim_number].interpolation>>8) + 9;
			gar_RotYXZsuperpack(&rotation, 11);
			PutPolyLara( UARM_L, clip );
			phd_TranslateRel( *(bone+1+44), *(bone+2+44), *(bone+3+44) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( LARM_L, clip );
			phd_TranslateRel( *(bone+1+48), *(bone+2+48), *(bone+3+48) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( HAND_L, clip );

			if ( lara.left_arm.flash_gun )				  	// Handle Gun Flashes last
				DrawGunFlash( fire_arms, clip );          	// as they alter the lighting
			if ( lara.right_arm.flash_gun )
			{
				S_LongMemCpy( phd_mxptr,savemox,12 );
				DrawGunFlash( fire_arms, clip );
			}

		break;

		case LG_SHOTGUN:
		case LG_HARPOON:
		case LG_ROCKET:
		case LG_M16:
			phd_PushMatrix();
			phd_TranslateRel( *(bone+1+28), *(bone+2+28), *(bone+3+28) );
			rotation = lara.right_arm.frame_base + lara.right_arm.frame_number * (anims[lara.right_arm.anim_number].interpolation>>8) + 9;
			gar_RotYXZsuperpack(&rotation, 8);
			PutPolyLara( UARM_R, clip );
			phd_TranslateRel( *(bone+1+32), *(bone+2+32), *(bone+3+32) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( LARM_R, clip );
			phd_TranslateRel( *(bone+1+36), *(bone+2+36), *(bone+3+36) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( HAND_R, clip );
			if ( lara.right_arm.flash_gun )
				S_LongMemCpy( savemox,phd_mxptr,12 );	// If Firing Gun save where Gun Flash is....

			phd_PopMatrix();
			phd_PushMatrix();
			phd_TranslateRel( *(bone+1+40), *(bone+2+40), *(bone+3+40) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( UARM_L, clip );
			phd_TranslateRel( *(bone+1+44), *(bone+2+44), *(bone+3+44) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( LARM_L, clip );
			phd_TranslateRel( *(bone+1+48), *(bone+2+48), *(bone+3+48) );
			gar_RotYXZsuperpack(&rotation, 0);
			PutPolyLara( HAND_L, clip );

			if ( lara.right_arm.flash_gun )
			{
				S_LongMemCpy( phd_mxptr,savemox,12 );
				DrawGunFlash( fire_arms, clip );
			}
		break;
	}
	phd_PopMatrix();
	phd_PopMatrix();
	phd_PopMatrix();

	phd_left = left;                    	// Reset bounds to what they were
	phd_right = right;
	phd_top = top;
	phd_bottom = bottom;

	if (do_flare)
	{
		PHD_VECTOR pos;
		long	xv,yv,zv,lp;

		pos.x = 8;
		pos.y = 36;
		pos.z = 32;
		GetLaraJointAbsPosition(&pos,13);	// Get X, Y & Z coordinates.
		xv = pos.x;
		yv = pos.y;
		zv = pos.z;

		pos.x = 8;
		pos.y = 36;
		pos.z = 1024+(rand()&255);
		GetLaraJointAbsPosition(&pos,13);	// Get X,Y & Z velocities.

		for (lp=0;lp<(rand()&3)+4;lp++)
			TriggerFlareSparks(xv,yv,zv,pos.x-xv,pos.y-yv,pos.z-zv);
	}
}


/**********************************************************************************************
 **********************************************************************************************
 *
 *						Interpolation  Stuff !!!!
 *
 **********************************************************************************************
 *********************************************************************************************/

sint32	*IMptr;
sint32	IMstack[12*32];
sint32	IM_frac;
sint32	IM_rate;

/*****************************************************************************
 *				Draw Lara special routines....
 ****************************************************************************/
void	DrawLaraInt( ITEM_INFO *item, sint16 *frame1, sint16 *frame2, int frac, int rate )
{
	OBJECT_INFO *object;
	sint32 		*bone, *bonew;
	sint16		*rotation1, *rotation2, *rotationw1, *rotationw2;
	sint16		*bounds;
	int			clip,fire_arms;
	sint32		savemox1[12];
	long		do_flare=0;

	object = &objects[item->object_number];
	bounds = GetBoundsAccurate( item );

	if (lara.skidoo == NO_ITEM)
		S_PrintShadow( object->shadow_size,bounds,item,0 );		// Lara always has a shadow..

	S_LongMemCpy( savemox1,phd_mxptr,12 ); // save this matrix for hair

	phd_PushMatrix();
	phd_TranslateAbs(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);

//	DrawBox(frame1, C_WHITE);
	if ( !(clip=S_GetObjectBounds(frame1)) )
	{
		phd_PopMatrix();
		return;
	}
	phd_PushMatrix();

	CalculateObjectLightingLara();

	bone = bones + object->bone_index;
	rotation1 = frame1+9;
	rotation2 = frame2+9;

	InitInterpolate(frac,rate);

	phd_TranslateRel_ID( (sint32)*(frame1+6), (sint32)*(frame1+7), (sint32)*(frame1+8),
					     (sint32)*(frame2+6), (sint32)*(frame2+7), (sint32)*(frame2+8) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( HIPS, clip );

	phd_PushMatrix_I();
	phd_TranslateRel_I( *(bone+1), *(bone+2), *(bone+3) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( THIGH_L, clip );

	phd_TranslateRel_I( *(bone+1+4), *(bone+2+4), *(bone+3+4) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( CALF_L, clip );

	phd_TranslateRel_I( *(bone+1+8), *(bone+2+8), *(bone+3+8) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( FOOT_L, clip );
	phd_PopMatrix_I();

	phd_PushMatrix_I();
	phd_TranslateRel_I( *(bone+1+12), *(bone+2+12), *(bone+3+12) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( THIGH_R, clip );

	phd_TranslateRel_I( *(bone+1+16), *(bone+2+16), *(bone+3+16) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( CALF_R, clip );

	phd_TranslateRel_I( *(bone+1+20), *(bone+2+20), *(bone+3+20) );
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
	PutPolyLara_I( FOOT_R, clip );
	phd_PopMatrix_I();				// Torso..

	phd_TranslateRel_I( *(bone+1+24), *(bone+2+24), *(bone+3+24) );

	if (lara.weapon_item!=NO_ITEM && lara.gun_type==LG_M16 &&
		 (items[lara.weapon_item].current_anim_state==0 || items[lara.weapon_item].current_anim_state==2 || items[lara.weapon_item].current_anim_state==4))
	{
		rotation1 = rotation2 = lara.right_arm.frame_base + lara.right_arm.frame_number * (anims[lara.right_arm.anim_number].interpolation>>8) + 9;
		gar_RotYXZsuperpack_I(&rotation1, &rotation2, 7);
	}
	else
		gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

	phd_RotYXZ_I( lara.torso_y_rot, lara.torso_x_rot, lara.torso_z_rot );
	PutPolyLara_I( TORSO, clip );

	phd_PushMatrix_I();               // Head
	phd_TranslateRel_I( *(bone+1+52), *(bone+2+52), *(bone+3+52) );
	rotationw1 = rotation1;
	rotationw2 = rotation2;
	gar_RotYXZsuperpack_I(&rotation1, &rotation2, 6);
	rotation1 = rotationw1;
	rotation2 = rotationw2;
	phd_RotYXZ_I( lara.head_y_rot,lara.head_x_rot,lara.head_z_rot );
	PutPolyLara_I( HEAD, clip );

	S_LongMemCpy( phd_mxptr,savemox1,12 ); // get back matrix and do hair
	DrawHair();
	phd_PopMatrix_I();

	if (lara.back_gun)
	{
		// lara.back_gun is the object number of the gun that is on Lara's back at present (if any)
		phd_PushMatrix_I();
		bonew = bones + objects[lara.back_gun].bone_index;
		phd_TranslateRel_I( *(bonew+1+52), *(bonew+2+52), *(bonew+3+52) );
		rotationw1 = rotationw2 = objects[lara.back_gun].frame_base + 9;
		gar_RotYXZsuperpack_I(&rotationw1, &rotationw2, 14);
		phd_PutPolygons_I(meshes[objects[lara.back_gun].mesh_index + HEAD], clip);
		phd_PopMatrix_I();
	}

	fire_arms = LG_UNARMED;						// Get what Gun is currently in Use
	if ( lara.gun_status==LG_READY ||       	// if Guns are Away dont use Animations...
		 lara.gun_status==LG_SPECIAL ||
		 lara.gun_status==LG_DRAW ||
		 lara.gun_status==LG_UNDRAW )
		 	fire_arms = lara.gun_type;

	switch ( fire_arms )
	{
		case LG_UNARMED:					// UNARMED
		case LG_FLARE:
			phd_PushMatrix_I();
			phd_TranslateRel_I( *(bone+1+28),*(bone+2+28),*(bone+3+28) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( UARM_R, clip );					// Right Arm insert
			phd_TranslateRel_I( *(bone+1+32), *(bone+2+32), *(bone+3+32) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( LARM_R, clip );
			phd_TranslateRel_I( *(bone+1+36), *(bone+2+36), *(bone+3+36) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( HAND_R, clip );
			phd_PopMatrix_I();

			phd_PushMatrix_I();
			phd_TranslateRel_I( *(bone+1+40), *(bone+2+40), *(bone+3+40) );

			if ( lara.flare_control_left )
			{
				rotation1 = rotation2 = lara.left_arm.frame_base + (lara.left_arm.frame_number - anims[lara.left_arm.anim_number].frame_base)
																						* (anims[lara.left_arm.anim_number].interpolation>>8) + 9;
				gar_RotYXZsuperpack_I(&rotation1, &rotation2, 11);
			}
			else
				gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

			PutPolyLara_I( UARM_L, clip );
			phd_TranslateRel_I( *(bone+1+44), *(bone+2+44), *(bone+3+44) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( LARM_L, clip );
			phd_TranslateRel_I( *(bone+1+48), *(bone+2+48), *(bone+3+48) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( HAND_L, clip );

			// plonk the flare burn on left hand
			if (lara.gun_type==LG_FLARE && lara.left_arm.flash_gun)
			{
				phd_TranslateRel_I( 11,32,80 );
				phd_RotX_I(-90*ONE_DEGREE);
#ifdef PSX_VERSION
				if (!game_paused)
#endif
//				phd_RotY_I((sint16)(GetRandomDraw()*2));
				S_CalculateStaticLight(8*256);
//				phd_PutPolygons_I( meshes[objects[FLARELIGHT].mesh_index],clip );
#ifdef PSX_VERSION
				if (game_paused)
					S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_SHADE|SPRITE_TINT|SPRITE_COLOUR(0xff,0x80,0x80),0,0,0,objects[GLOW].mesh_index,0x1000,0);
				else
					S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_SHADE|SPRITE_TINT|SPRITE_COLOUR(0xff,0x80,0x80),0,0,0,objects[GLOW].mesh_index,(GetRandomDraw()&0xfff)+0x1000,0);
				do_flare = 1;
#endif
			}
			break;

		case LG_PISTOLS:
		case LG_MAGNUMS:
		case LG_UZIS:
			phd_PushMatrix_I();
			phd_TranslateRel_I( *(bone+1+28), *(bone+2+28), *(bone+3+28) );
			InterpolateArmMatrix();
			phd_RotYXZ( lara.right_arm.y_rot, lara.right_arm.x_rot, lara.right_arm.z_rot );
			rotation1 = lara.right_arm.frame_base + (lara.right_arm.frame_number - anims[lara.right_arm.anim_number].frame_base)
																	* (anims[lara.right_arm.anim_number].interpolation>>8) + 9;
			gar_RotYXZsuperpack(&rotation1, 8);
			PutPolyLara( UARM_R, clip );
			phd_TranslateRel( *(bone+1+32), *(bone+2+32), *(bone+3+32) );
			gar_RotYXZsuperpack(&rotation1, 0);
			PutPolyLara( LARM_R, clip );
			phd_TranslateRel( *(bone+1+36), *(bone+2+36), *(bone+3+36) );
			gar_RotYXZsuperpack(&rotation1, 0);
			PutPolyLara( HAND_R, clip );
			if ( lara.right_arm.flash_gun )
				S_LongMemCpy( savemox1,phd_mxptr,12 );		// If Firing Gun save where Gun Flash is....

			phd_PopMatrix_I();
			phd_PushMatrix_I();
			phd_TranslateRel_I( *(bone+1+40), *(bone+2+40), *(bone+3+40) );
			InterpolateArmMatrix();
			phd_RotYXZ( lara.left_arm.y_rot, lara.left_arm.x_rot, lara.left_arm.z_rot );
			rotation1 = lara.left_arm.frame_base + (lara.left_arm.frame_number - anims[lara.left_arm.anim_number].frame_base)
																	* (anims[lara.left_arm.anim_number].interpolation>>8) + 9;
			gar_RotYXZsuperpack(&rotation1, 11);
			PutPolyLara( UARM_L, clip );
			phd_TranslateRel( *(bone+1+44), *(bone+2+44), *(bone+3+44) );
			gar_RotYXZsuperpack(&rotation1, 0);
			PutPolyLara( LARM_L, clip );
			phd_TranslateRel( *(bone+1+48), *(bone+2+48), *(bone+3+48) );
			gar_RotYXZsuperpack(&rotation1, 0);
			PutPolyLara( HAND_L, clip );

			if ( lara.left_arm.flash_gun )				  	// Handle Gun Flashes last
				DrawGunFlash( fire_arms, clip );          	// as they alter the lighting
			if ( lara.right_arm.flash_gun )
			{
				S_LongMemCpy( phd_mxptr,savemox1,12 );
				DrawGunFlash( fire_arms, clip );
			}
			break;

		case LG_SHOTGUN:
		case LG_HARPOON:
		case LG_ROCKET:
		case LG_M16:
			phd_PushMatrix_I();
			phd_TranslateRel_I( *(bone+1+28), *(bone+2+28), *(bone+3+28) );
			rotation1 = rotation2 = lara.right_arm.frame_base + lara.right_arm.frame_number * (anims[lara.right_arm.anim_number].interpolation>>8) + 9;
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 8);
			PutPolyLara_I( UARM_R, clip );
			phd_TranslateRel_I( *(bone+1+32), *(bone+2+32), *(bone+3+32) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( LARM_R, clip );
			phd_TranslateRel_I( *(bone+1+36), *(bone+2+36), *(bone+3+36) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( HAND_R, clip );
			if ( lara.right_arm.flash_gun )
				S_LongMemCpy( savemox1,phd_mxptr,12 );		// If Firing Gun save where Gun Flash is....
			phd_PopMatrix_I();

			phd_PushMatrix_I();
			phd_TranslateRel_I( *(bone+1+40), *(bone+2+40), *(bone+3+40) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( UARM_L, clip );
			phd_TranslateRel_I( *(bone+1+44), *(bone+2+44), *(bone+3+44) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( LARM_L, clip );
			phd_TranslateRel_I( *(bone+1+48), *(bone+2+48), *(bone+3+48) );
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);
			PutPolyLara_I( HAND_L, clip );

			if ( lara.right_arm.flash_gun )
			{
				S_LongMemCpy( phd_mxptr,savemox1,12 );
				DrawGunFlash( fire_arms, clip );
			}
			break;
	}

	phd_PopMatrix();
	phd_PopMatrix();
	phd_PopMatrix();


// Find X,Y,Z of end of flare to trigger particles.

	if (do_flare)
	{
		PHD_VECTOR pos;
		long	xv,yv,zv;

		pos.x = 8;
		pos.y = 36;
		pos.z = 32;
		GetLaraJointAbsPosition(&pos,13);	// Get X, Y & Z coordinates.
		xv = pos.x;
		yv = pos.y;
		zv = pos.z;

		pos.x = 8;
		pos.y = 36;
		pos.z = 1024+(rand()&255);
		GetLaraJointAbsPosition(&pos,13);	// Get X,Y & Z velocities.
		TriggerFlareSparks(xv,yv,zv,pos.x-xv,pos.y-yv,pos.z-zv);
	}
}

/******************************************************************************
 *			Matrix Interpolation routines
 *****************************************************************************/
void	InitInterpolate( int frac, int rate )
{
	IM_frac = frac;
	IM_rate = rate;

	IMptr = IMstack;
	S_LongMemCpy( IMstack,phd_mxptr,12 );
}

void	phd_PopMatrix_I(void)
{
	phd_PopMatrix();
	IMptr -= 12;
}

void	phd_PushMatrix_I(void)
{
	phd_PushMatrix();
	S_LongMemCpy( IMptr+12,IMptr,12 );
	IMptr += 12;
}

void	phd_RotY_I( sint16 ang )
{
	sint32	*oldm;

	phd_RotY( ang );
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	phd_RotY( ang );
	phd_mxptr = oldm;
}

void	phd_RotX_I( sint16 ang )
{
	sint32	*oldm;

	phd_RotX( ang );
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	phd_RotX( ang );
	phd_mxptr = oldm;
}

void	phd_RotZ_I( sint16 ang )
{
	sint32	*oldm;

	phd_RotZ( ang );
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	phd_RotZ( ang );
	phd_mxptr = oldm;
}

void	phd_TranslateRel_I( sint32 x, sint32 y, sint32 z )
{
	sint32	*oldm;

	phd_TranslateRel( x,y,z );
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	phd_TranslateRel( x,y,z );
	phd_mxptr = oldm;
}


void	phd_TranslateRel_ID( sint32 x, sint32 y, sint32 z, sint32 x2, sint32 y2, sint32 z2 )
{
	sint32	*oldm;

	phd_TranslateRel( x,y,z );
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	phd_TranslateRel( x2,y2,z2 );
	phd_mxptr = oldm;
}


void	phd_RotYXZ_I( sint16 y, sint16 x, sint16 z )
{
	sint32	*oldm;

	phd_RotYXZ( y,x,z );
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	phd_RotYXZ( y,x,z );
	phd_mxptr = oldm;
}


void gar_RotYXZsuperpack_I(sint16 **pprot1, sint16 **pprot2, int skip)
{
	sint32 *oldm;

	gar_RotYXZsuperpack(pprot1, skip);
	oldm = phd_mxptr;
	phd_mxptr = IMptr;
	gar_RotYXZsuperpack(pprot2, skip);
	phd_mxptr = oldm;
}

void gar_RotYXZsuperpack(sint16 **pprot, int skip)
{
	uint16 *prot;
	sint32 packed;

	/* Can skip over rots to get to one we want */
	while (skip)
	{
		prot = *pprot;
		if (*prot & (3<<14))
			(*pprot)+=1;
		else
			(*pprot)+=2;
		skip--;
	}

	/* Check top bits; 0 -> YXZpack, 1 -> X, 2 -> y, 3 -> Z
		Returns if 1 or 2 words to be skipped */
	prot = *pprot;
	switch (*prot >> 14)
	{
	case 0:
		packed = (*prot<<16) + *(prot+1);
		phd_RotYXZpack(packed);
		(*pprot)+=2;
		return;
	case 1:
		phd_RotX((sint16)((*prot & 1023) << 6));
		break;
	case 2:
		phd_RotY((sint16)((*prot & 1023) << 6));
		break;
	default:
		phd_RotZ((sint16)((*prot & 1023) << 6));
		break;
	}

	(*pprot)+=1;
}


void	phd_PutPolygons_I( sint16 *ptr, int clip )
{
	phd_PushMatrix();
	InterpolateMatrix();
	phd_PutPolygons( ptr,clip );
	phd_PopMatrix();
}

/*****************************************************************************
 *			Interpolate Two Draw Matrices
 ****************************************************************************/
#ifndef USE_ASM
void	InterpolateMatrix( void )
{
	sint32	*mptr,*iptr;
	sint32	frac,rate;

	mptr = phd_mxptr;
	iptr = IMptr;
	frac = IM_frac;
	rate = IM_rate;
	if ( rate==2 )                                           // If interpolating Odd Frames
	{                                                        // then do simple case
		*(mptr+M00) = (*(iptr+M00) + *(mptr+M00))>>1;
		*(mptr+M01) = (*(iptr+M01) + *(mptr+M01))>>1;
		*(mptr+M02) = (*(iptr+M02) + *(mptr+M02))>>1;
		*(mptr+M03) = (*(iptr+M03) + *(mptr+M03))>>1;

		*(mptr+M10) = (*(iptr+M10) + *(mptr+M10))>>1;
		*(mptr+M11) = (*(iptr+M11) + *(mptr+M11))>>1;
		*(mptr+M12) = (*(iptr+M12) + *(mptr+M12))>>1;
		*(mptr+M13) = (*(iptr+M13) + *(mptr+M13))>>1;

		*(mptr+M20) = (*(iptr+M20) + *(mptr+M20))>>1;
		*(mptr+M21) = (*(iptr+M21) + *(mptr+M21))>>1;
		*(mptr+M22) = (*(iptr+M22) + *(mptr+M22))>>1;
		*(mptr+M23) = (*(iptr+M23) + *(mptr+M23))>>1;
	}
	else
	{
		*(mptr+M00) += ((*(iptr+M00) - *(mptr+M00))*frac)/rate;
		*(mptr+M01) += ((*(iptr+M01) - *(mptr+M01))*frac)/rate;
		*(mptr+M02) += ((*(iptr+M02) - *(mptr+M02))*frac)/rate;
		*(mptr+M03) += ((*(iptr+M03) - *(mptr+M03))*frac)/rate;

		*(mptr+M10) += ((*(iptr+M10) - *(mptr+M10))*frac)/rate;
		*(mptr+M11) += ((*(iptr+M11) - *(mptr+M11))*frac)/rate;
		*(mptr+M12) += ((*(iptr+M12) - *(mptr+M12))*frac)/rate;
		*(mptr+M13) += ((*(iptr+M13) - *(mptr+M13))*frac)/rate;

		*(mptr+M20) += ((*(iptr+M20) - *(mptr+M20))*frac)/rate;
		*(mptr+M21) += ((*(iptr+M21) - *(mptr+M21))*frac)/rate;
		*(mptr+M22) += ((*(iptr+M22) - *(mptr+M22))*frac)/rate;
		*(mptr+M23) += ((*(iptr+M23) - *(mptr+M23))*frac)/rate;
	}
}

#endif

/*****************************************************************************
 *			Interpolate Two Draw Matrices
 ****************************************************************************/
void	InterpolateArmMatrix( void )
{
	sint32	*mptr,*iptr;
	sint32	frac,rate;

	mptr = phd_mxptr;
	iptr = IMptr;
	frac = IM_frac;
	rate = IM_rate;
	if ( rate==2 )                                           // If interpolating Odd Frames
	{                                                        // then do simple case
		*(mptr+M00) = *(mptr+M00-24);
		*(mptr+M01) = *(mptr+M01-24);
		*(mptr+M02) = *(mptr+M02-24);
		*(mptr+M03) = (*(iptr+M03) + *(mptr+M03))/2;
		*(mptr+M10) = *(mptr+M10-24);
		*(mptr+M11) = *(mptr+M11-24);
		*(mptr+M12) = *(mptr+M12-24);
		*(mptr+M13) = (*(iptr+M13) + *(mptr+M13))/2;
		*(mptr+M20) = *(mptr+M20-24);
		*(mptr+M21) = *(mptr+M21-24);
		*(mptr+M22) = *(mptr+M22-24);
		*(mptr+M23) = (*(iptr+M23) + *(mptr+M23))/2;
	}
	else
	{
		*(mptr+M00) = *(mptr+M00-24);
		*(mptr+M01) = *(mptr+M01-24);
		*(mptr+M02) = *(mptr+M02-24);
		*(mptr+M03) += ((*(iptr+M03) - *(mptr+M03))*frac)/rate;
		*(mptr+M10) = *(mptr+M10-24);
		*(mptr+M11) = *(mptr+M11-24);
		*(mptr+M12) = *(mptr+M12-24);
		*(mptr+M13) += ((*(iptr+M13) - *(mptr+M13))*frac)/rate;
		*(mptr+M20) = *(mptr+M20-24);
		*(mptr+M21) = *(mptr+M21-24);
		*(mptr+M22) = *(mptr+M22-24);
		*(mptr+M23) += ((*(iptr+M23) - *(mptr+M23))*frac)/rate;
	}
}

/******************************************************************************
 *			Draw Rotating Gun Flash at End Of Gun Please...
 *****************************************************************************/
void	DrawGunFlash( int weapon_type, int clip )
{
	sint16	light;
	int		g_len,nZOff;

	switch ( weapon_type )
	{
	case LG_FLARE:
		phd_TranslateRel( 11,32,80 );
		phd_RotX(-90*ONE_DEGREE);
#ifdef PSX_VERSION
		if (!game_paused)
#endif
//		phd_RotY((sint16)(GetRandomDraw()*2));
		S_CalculateStaticLight(8*256);
//		phd_PutPolygons( meshes[objects[FLARELIGHT].mesh_index],clip );
#ifdef PSX_VERSION
		if (game_paused)
			S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_SHADE|SPRITE_TINT|SPRITE_COLOUR(0xff,0x80,0x80),0,0,0,objects[GLOW].mesh_index,0x1000,0);
		else
			S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_SHADE|SPRITE_TINT|SPRITE_COLOUR(0xff,0x80,0x80),0,0,0,objects[GLOW].mesh_index,(GetRandomDraw()&0xfff)+0x1000,0);
#endif
		return;

	case LG_MAGNUMS:
		light = 16*256;
		g_len = 215;
		nZOff=65;
		break;

	case LG_UZIS:
		light = 10*256;
		g_len = 200;
		nZOff=50;
		break;

	case LG_M16:
		phd_TranslateRel( 0,400,99 );
#ifdef PSX_VERSION
		if (game_paused)
			phd_RotYXZ( 0, -85*ONE_DEGREE, 0x4000);
		else
#endif
		phd_RotYXZ( 0, -85*ONE_DEGREE, (sint16)((GetRandomDraw()*2 & 0x4000) + 0x2000));
		S_CalculateStaticLight( 10*256 );
		phd_PutPolygons( meshes[objects[M16_FLASH].mesh_index],clip );
#ifdef PSX_VERSION
		S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_TINT|SPRITE_COLOUR(0x7f,0x70,0x1f)|SPRITE_SCALE,0,0,-65,objects[GLOW].mesh_index,0,0x200);
#endif
		return;

	case LG_SHOTGUN:
		return;

	default:
		light = 20*256;
		g_len=185;
		nZOff=40;
		break;
	}
	phd_TranslateRel( 0,g_len,nZOff);
#ifdef PSX_VERSION
	if (game_paused)
		phd_RotYXZ( 0, -90*ONE_DEGREE, 0 );
	else
#endif
	phd_RotYXZ( 0, -90*ONE_DEGREE, (sint16)(GetRandomDraw()*2) );
	S_CalculateStaticLight( light );
	phd_PutPolygons( meshes[objects[GUN_FLASH].mesh_index],clip );
#ifdef PSX_VERSION
	S_DrawSprite(SPRITE_REL|SPRITE_SEMITRANS|SPRITE_TRANS_ADD|SPRITE_TINT|SPRITE_COLOUR(0x3f,0x38,0x0f)|SPRITE_SCALE,0,0,0,objects[GLOW].mesh_index,0,0x200);
#endif
}

/******************************************************************************
 *		Calculate light sourcing for Item at middle of Bounds
 *****************************************************************************/
void	CalculateObjectLighting( ITEM_INFO *item, sint16 *frame )
{
	sint32	x,y,z;
	sint32	*mptr;

	/* If shade is -1 then dynamic light calculation needed; else use static lighting */
	if (item->shade < 0)
	{
		phd_PushUnitMatrix();
		mptr = phd_mxptr;
		*(mptr+M03) = *(mptr+M13) = *(mptr+M23) = 0;
		phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);
		x = (sint32)( (*(frame+0)+*(frame+1))>>1 );
		y = (sint32)( (*(frame+2)+*(frame+3))>>1 );
		z = (sint32)( (*(frame+4)+*(frame+5))>>1 );
		phd_TranslateRel( x,y,z );
		x = (*(phd_mxptr+M03)>>W2V_SHIFT) + item->pos.x_pos;
		y = (*(phd_mxptr+M13)>>W2V_SHIFT) + item->pos.y_pos;
		z = (*(phd_mxptr+M23)>>W2V_SHIFT) + item->pos.z_pos;
		phd_PopMatrix();
		S_CalculateLight( x,y,z,item->room_number );
	}
	else
		S_CalculateStaticMeshLight(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->shade, item->shadeB, &room[item->room_number]);
}

void	CalculateObjectLightingLara()
{
	PHD_VECTOR pos;
	short	room_no;

	if (lara_item->current_anim_state == AS_STOP || lara_item->current_anim_state == AS_DUCK)
	{
		pos.x = lara_item->pos.x_pos;
		pos.y = lara_item->pos.y_pos;
		pos.z = lara_item->pos.z_pos;
		room_no = lara_item->room_number;
	}
	else
	{
		pos.x = 0;
		pos.y = 0;
		pos.z = 0;
		GetLaraHandAbsPosition(&pos, LARA_TORSO);
		room_no = lara_item->room_number;
		GetFloor(pos.x, pos.y, pos.z, (short *) &room_no);
	}

	S_CalculateLightLara( pos.x,pos.y,pos.z,room_no);
}

/********************************************************************
 *		For any Item get interpolation frames fraction,rate
 *******************************************************************/
int		GetFrames( ITEM_INFO *item, sint16 *frmptr[], int *rate )
{
	ANIM_STRUCT *anim;
	int			frm;
	int			first,second;
	int			frame_size;
	int			interp,rat;

	frm = item->frame_number;
	anim = &anims[item->anim_number];
	frmptr[0] = frmptr[1] = anim->frame_ptr;
	rat = *rate = anim->interpolation & 0x00ff;
	frame_size = anim->interpolation >> 8;
	frm -= anim->frame_base;
	first =  frm/rat;
	interp = frm%rat;
	frmptr[0] += first * frame_size;				  // Get Frame pointers
	frmptr[1] = frmptr[0] + frame_size;               // and store away
	if ( interp==0 )
		return(0);
	second = first*rat + rat;
	if ( second>anim->frame_end )                       // Clamp KeyFrame to End if need be
		*rate = anim->frame_end-(second-rat);
	return(interp);
}

/******************************************************************************
 *		Get Interpolated Bounds for particular item
 *****************************************************************************/
sint16	interpolated_bounds[6];

sint16	*GetBoundsAccurate( ITEM_INFO *item )
{
	int		i,frac,rate;
	sint16	*bptr,*frmptr[2];

	if ( (frac=GetFrames( item,frmptr,&rate ))==0 )
		return( frmptr[0] );
	else
	{
		bptr = interpolated_bounds;
		for ( i=0; i<6;i ++,bptr++,frmptr[0]++,frmptr[1]++ )
			*(bptr) = *(frmptr[0]) + ((*(frmptr[1])-*(frmptr[0]))*frac)/rate;
		return( interpolated_bounds );
	}
}


/******************************************************************************
 *			Get Nearest Frame to interpolation
 *****************************************************************************/
sint16	*GetBestFrame( ITEM_INFO *item )
{
	int		frac,rate;
	sint16	*frmptr[2];

	frac = GetFrames( item,frmptr,&rate );
	if ( frac<=rate/2 )
		return( frmptr[0] );
	else
		return( frmptr[1] );
}


/* Dynamic light stuff.

	'S_LightRoom' is in 'output.c'

	In addition, 'S_CalculateLight' in 'output.c' has been modified to include the dynamic lights
	in its calculation of lighting for moving objects.

	'number_dynamics' is cleared to 0 at start of each frame in 'control.c' */

void AddDynamicLight(sint32 x, sint32 y, sint32 z, sint32 intensity, sint32 falloff)
{
	/* Add a dynamic light to the list for this frame. Note that both intensity and
		falloff are passed as bits shifts (so actual intensity is (1<<intensity)) to
		speed up the calculation of dynamic gouraud shades in rooms */
	LIGHT_INFO *dl;

	return;

	if (number_dynamics >= MAX_DYNAMICS)
		dl = dynamic; // steal the first slot for this light (ensures Lara's held flare is OK)
	else
		dl = &dynamic[number_dynamics++];

	dl->x = x;
	dl->y = y;
	dl->z = z;
	dl->intensity = (sint16)intensity;
	dl->falloff = falloff;
}


#ifdef GAMEDEBUG
SG_COL RedBox[]={S_GOURAUD_COL(255,  0,  0,128),
					   S_GOURAUD_COL(255,  0,  0,128),
					   S_GOURAUD_COL(255,  0,  0,128),
					   S_GOURAUD_COL(255,  0,  0,128)};
SG_COL GreenBox[]={S_GOURAUD_COL(  0,255,  0,128),
				   S_GOURAUD_COL(  0,255,  0,128),
				   S_GOURAUD_COL(  0,255,  0,128),
				   S_GOURAUD_COL(  0,255,  0,128)};
SG_COL BlueBox[]={S_GOURAUD_COL(0,  0,255,128),
				  S_GOURAUD_COL(0,  0,255,128),
				  S_GOURAUD_COL(0,  0,255,128),
				  S_GOURAUD_COL(0,  0,255,128)};
SG_COL YellowBox[]={S_GOURAUD_COL(255,255,  0,128),
					S_GOURAUD_COL(255,255,  0,128),
					S_GOURAUD_COL(255,255,  0,128),
					S_GOURAUD_COL(255,255,  0,128)};

void DrawRoomBox(int room_num)
{
	sint16	bounds[6];
	ROOM_INFO *r;

	r = &room[room_num];

	phd_PushMatrix();
	phd_TranslateAbs(r->x, r->y, r->z);

	// Set bounds and put this room's mesh on list
	phd_left = r->left;
	phd_right = r->right;
	phd_top = r->top;
	phd_bottom = r->bottom;


	/* Fill in the coordinates of the room */
	bounds[0] = WALL_L;							// min x
	bounds[2] = (sint16)(r->maxceiling - r->y);
	bounds[4] = WALL_L;							// min z

	bounds[1] = (r->y_size-1) * WALL_L;		// max x
	bounds[3] = (sint16)(r->minfloor - r->y);				// max y
	bounds[5] = (r->x_size-1) * WALL_L;		// max z


	if (room_num==lara_item->room_number)
		DrawBox(bounds, C_WHITE);
	else
		DrawBox(bounds, C_BLUE);

	S_DrawScreenBox(phd_left,phd_top,0,phd_right-phd_left,phd_bottom-phd_top,C_RED,RedBox,0);

	phd_PopMatrix();
}

void	DrawBox(sint16 *bptr, int colour)
{
	int	p1,p2,i;
	sint32 xv[8], yv[8], zv[8];	//, clip[8];
	sint32 x, y, z, *mptr;
	sint32 xs[8], ys[8];

	// Set bounds and put this room's mesh on list
	phd_left = phd_top = 0;
	phd_right = phd_winxmax;
	phd_bottom = phd_winymax;

	/* Fill in the coordinates of the room */
	xv[0] = xv[3] = xv[4] = xv[7] = *bptr;							// min x
	yv[0] = yv[1] = yv[2] = yv[3] = *(bptr+2);
	zv[0] = zv[1] = zv[4] = zv[5] = *(bptr+4);							// min z

	xv[1] = xv[2] = xv[5] = xv[6] = *(bptr+1);
	yv[4] = yv[5] = yv[6] = yv[7] = *(bptr+3);
	zv[2] = zv[3] = zv[6] = zv[7] = *(bptr+5);

	mptr = phd_mxptr;

	/* Convert coords to viewspace */
	for (i=0; i<8; i++)
	{
		x = xv[i];
		y = yv[i];
		z = zv[i];

		xv[i] = (*(mptr+M00) * x) + (*(mptr+M01) * y) + (*(mptr+M02) * z) + *(mptr+M03);
		yv[i] = (*(mptr+M10) * x) + (*(mptr+M11) * y) + (*(mptr+M12) * z) + *(mptr+M13);
		zv[i] = (*(mptr+M20) * x) + (*(mptr+M21) * y) + (*(mptr+M22) * z) + *(mptr+M23);
		z = zv[i]/phd_persp;
		xs[i] = xv[i]/z +phd_centerx;
		ys[i] = yv[i]/z +phd_centery;
	}
	for (i=0;i<12;i++)
	{
		p1 = box_lines[i][0];
		p2 = box_lines[i][1];
		if (zv[p1]>phd_znear && zv[p2]>phd_znear && zv[p1]<phd_zfar && zv[p2]<phd_zfar)
			S_DrawLine(xs[p1],ys[p1],0, xs[p2],ys[p2], colour, YellowBox, 0 );
	}
}
#endif


