/****************************************************************************************************/
/*                                                                                                  */
/* Span Drawer                                                                       G.Rummery 1997 */
/*                                                                                                  */
/****************************************************************************************************/

#include "3dglodef.h"

/************************************** SPAN DRAWER *************************************************/

/* Mask for deciding whether to single or double pixel */
#define SINGLE_MASK 0xffff8000


VERTEX_INFO v_buffer[20];		// 20 possible clipped points

#define MAX_LINES 480
#define MAX_EDGES 6000
#define MAX_SPANS 1000

#define NOT_VISIBLE -1
#define VISIBLE 0

/* Polygon face types */
#define OPAQUE_TYPE      0
#define TRANSPARENT_TYPE 1
#define BACKGROUND_TYPE  DRAW_POLY_LINE

typedef struct point_info {
	float xv, yv, zv;
	float xs, ys;
	float u, v;
	float g;
} POINT_INFO;

typedef struct edge_type {
	short id;
	char type;
	unsigned char colour;
	long x, x_add;
	long g, g_add;
	float uoz, uoz_add;
	float voz, voz_add;
	float ooz, ooz_add;
	long y;
	struct edge_type *prev, *next; // for double linked list on spare and AET lists
	struct edge_type *link; // other end of span
} EDGE;

EDGE edges[MAX_EDGES], *spare_edge;
EDGE *edge_table[MAX_LINES];

typedef struct span_type {
	EDGE *start_edge, *end_edge;
	long start_x, end_x;
	float ooz, ooz_add;
	struct span_type *next;
	struct span_type *draw_prev, *draw_next;
} SPAN;

SPAN spans[MAX_SPANS], *spare_span;

// Paul Store 1.0 as its quicker than generating it every time???
static float one  = (65536.0f * 8.0f * 16384.0f);               // gives OOZ in range 26214 to 25.6

/*********************************** FUNCTION CODE *********************************************/

void InitialiseEdgesAndSpans(void)
{
	int i;

	for (i=0; i<MAX_EDGES-1; i++)
		edges[i].next = &edges[i+1];
	edges[MAX_EDGES-1].next = NULL;
	spare_edge = edges;

	for (i=0; i<MAX_SPANS-1; i++)
		spans[i].next = &spans[i+1];
	spans[MAX_SPANS-1].next = NULL;
	spare_span = spans;

	/* Clear edge table for first time use */
	for (i=0; i<MAX_LINES; i++)
		edge_table[i] = NULL;
}


void InsertEdges(int npoints, sint16 tpage, int type)
{
	/* Edges should be inserted directly in poly insert routines after clipping. There should be
		no sorting and no going through a draw list. This routine is the one designed for inserting
		perspective texture maps, so does uoz,voz calcs. Sprites could just use uoz, voz as u, v instead. */
	long g, x;
	float scale;
	float uoz, voz, ooz;
	long y1, y2, ydif;
	VERTEX_INFO *ptr1, *ptr2;
	EDGE *edge;

	/* Face id number for finding matching edges */
	edge_id++;

	ptr2 = v_buffer;
	ptr1 = ptr2 + npoints-1;
	for ( ; npoints>0; npoints--, ptr1=ptr2, ptr2++)
	{
		y1 = (sint32)ptr1->y;
		y2 = (sint32)ptr2->y;
		if (y1 < y2) // RHS of poly => trailing edge
		{
			ydif = y2-y1;

			edge = spare_edge;
			if (edge == NULL)
				S_ExitSystem("No edges left");
			spare_edge = edge->next;

			edge->id = edge_id;
			edge->type = type;

			edge->colour = (unsigned char)tpage;

			x = (sint32)ptr1->x;
			edge->x_add = (((sint32)ptr2->x - x) << 16) / ydif;
			edge->x = (x<<16);

			edge->g = g = (sint32)ptr1->g << 8;
			scale = 1.0f/ydif;
			edge->g_add = (((sint32)ptr2->g << 8) - g)/ydif;
			edge->uoz = uoz = ptr1->u;
			edge->uoz_add = (ptr2->u - uoz) * scale;
			edge->voz = voz = ptr1->v;
			edge->voz_add = (ptr2->v - voz) * scale;
			edge->ooz = ooz = ptr1->ooz/256.0f;
			edge->ooz_add = (ptr2->ooz/256.0f - ooz) * scale;

			edge->y = ydif;

			edge->next = edge_table[y1];
			edge_table[y1] = edge;
		}
		else if (y2 < y1) // LHS of Polygon => leading edge
		{
			ydif = y1-y2;

			edge = spare_edge;
			if (edge == NULL)
				S_ExitSystem("No edges left");
			spare_edge = edge->next;

			edge->id = edge_id;
			edge->type = type;

			edge->colour = (unsigned char)tpage;

			x = (sint32)ptr2->x;
			edge->x_add = (((sint32)ptr1->x - x) << 16) / ydif;
			edge->x = (x<<16);

			edge->g = g = (sint32)ptr2->g << 8;
			scale = 1.0f/ydif;
			edge->g_add = (((sint32)ptr1->g << 8) - g)/ydif;
			edge->uoz = uoz = ptr2->u;
			edge->uoz_add = (ptr1->u - uoz) * scale;
			edge->voz = voz = ptr2->v;
			edge->voz_add = (ptr1->v - voz) * scale;
			edge->ooz = ooz = ptr2->ooz/256.0f;
			edge->ooz_add = (ptr1->ooz/256.0f - ooz) * scale;

			edge->y = ydif;

			edge->next = edge_table[y2];
			edge_table[y2] = edge;
		}
	}
}


sint16 *InsertTexture4(sint16 *objptr, int number, int persp_dist)
{
	/* Insert quad textures, clipping if necessary. Uses persp_dist to determine if polygons close enough to
		be drawn perspective correct or not. Allows objects to set persp_dist to enable or disable perspective
		correction. */
	/* NOTE: may want to automate persp_dist stuff be choosing whether to use perspective correction or not
		by the width of the polygon (i.e. if over 32 pixels then use it, else don't) */
	PHD_VBUF	*vn1, *vn2, *vn3, *vn4;
	PHDTEXTURESTRUCT *tex;
	POINT_INFO point[4];
	int npoints, type;
	float zdepth;

	for ( ; number>0; number--, objptr+=5)
	{
		vn1 = &vbuf[objptr[0]];
		vn2 = &vbuf[objptr[1]];
		vn3 = &vbuf[objptr[2]];
		vn4 = &vbuf[objptr[3]];
		if ( vn1->clip & vn2->clip & vn3->clip & vn4->clip )
			continue;

		if ( vn1->clip<0 || vn2->clip<0 || vn3->clip<0 || vn4->clip<0 )
		{
			if (!visible_zclip(vn1,vn2,vn3))
				continue;

			/* Need to Z-clip polygon first */
			tex = &phdtextinfo[objptr[4]];
			point[0].xv = vn1->xv;
			point[0].yv = vn1->yv;
			point[0].zv = vn1->zv;
			point[0].xs = vn1->xs;
			point[0].ys = vn1->ys;
			point[0].g = (float)vn1->g;
			point[0].u = (float)tex->u1;
			point[0].v = (float)tex->v1;

			point[1].xv = vn2->xv;
			point[1].yv = vn2->yv;
			point[1].zv = vn2->zv;
			point[1].xs = vn2->xs;
			point[1].ys = vn2->ys;
			point[1].g = (float)vn2->g;
			point[1].u = (float)tex->u2;
			point[1].v = (float)tex->v2;

			point[2].xv = vn3->xv;
			point[2].yv = vn3->yv;
			point[2].zv = vn3->zv;
			point[2].xs = vn3->xs;
			point[2].ys = vn3->ys;
			point[2].g = (float)vn3->g;
			point[2].u = (float)tex->u3;
			point[2].v = (float)tex->v3;

			point[3].xv = vn4->xv;
			point[3].yv = vn4->yv;
			point[3].zv = vn4->zv;
			point[3].xs = vn4->xs;
			point[3].ys = vn4->ys;
			point[3].g = (float)vn4->g;
			point[3].u = (float)tex->u4;
			point[3].v = (float)tex->v4;

			npoints = ZedClipper(4, point, v_buffer);
			if (npoints)
				npoints = XYGUVClipper(npoints, v_buffer);

			/* z clipped polys are always perspective correct */
			type = tex->drawtype + (DRAW_POLY_GTMAP_PERSPECTIVE-DRAW_POLY_GTMAP);
		}
		else
		{
			/* Don't need Z clipping (though may still need normal clipping) */
			IF_NOT_VIS(vn1,vn2,vn3)
				continue;

			tex = &phdtextinfo[objptr[4]];

			/* Choose whether this polygon will be perspective correct or not */
			QUAD_DEPTH_FAR(vn1,vn2,vn3,vn4);

			// if whole poly closer than perspective distance? how about if any part of poly?
			if (zdepth < persp_dist) 
			{
				type = tex->drawtype + (DRAW_POLY_GTMAP_PERSPECTIVE-DRAW_POLY_GTMAP);

				v_buffer[0].x = vn1->xs;
				v_buffer[0].y = vn1->ys;
				v_buffer[0].ooz = one / vn1->zv;
				v_buffer[0].u = tex->u1 * v_buffer[0].ooz;
				v_buffer[0].v = tex->v1 * v_buffer[0].ooz;
				v_buffer[0].g = (float)vn1->g;

				v_buffer[1].x = vn2->xs;
				v_buffer[1].y = vn2->ys;
				v_buffer[1].ooz = one / vn2->zv;
				v_buffer[1].u = tex->u2 * v_buffer[1].ooz;
				v_buffer[1].v = tex->v2 * v_buffer[1].ooz;
				v_buffer[1].g = (float)vn2->g;

				v_buffer[2].x = vn3->xs;
				v_buffer[2].y = vn3->ys;
				v_buffer[2].ooz = one / vn3->zv;
				v_buffer[2].u = tex->u3 * v_buffer[2].ooz;
				v_buffer[2].v = tex->v3 * v_buffer[2].ooz;
				v_buffer[2].g = (float)vn3->g;

				v_buffer[3].x = vn4->xs;
				v_buffer[3].y = vn4->ys;
				v_buffer[3].ooz = one / vn4->zv;
				v_buffer[3].u = tex->u4 * v_buffer[3].ooz;
				v_buffer[3].v = tex->v4 * v_buffer[3].ooz;
				v_buffer[3].g = (float)vn4->g;
			}
			else
			{
				type = tex->drawtype;

				v_buffer[0].x = vn1->xs;
				v_buffer[0].y = vn1->ys;
				v_buffer[0].ooz = one / vn1->zv;
				v_buffer[0].u = tex->u1 << 8;
				v_buffer[0].v = tex->v1 << 8;
				v_buffer[0].g = (float)vn1->g;

				v_buffer[1].x = vn2->xs;
				v_buffer[1].y = vn2->ys;
				v_buffer[1].ooz = one / vn2->zv;
				v_buffer[1].u = tex->u2 << 8;
				v_buffer[1].v = tex->v2 << 8;
				v_buffer[1].g = (float)vn2->g;

				v_buffer[2].x = vn3->xs;
				v_buffer[2].y = vn3->ys;
				v_buffer[2].ooz = one / vn3->zv;
				v_buffer[2].u = tex->u3 << 8;
				v_buffer[2].v = tex->v3 << 8;
				v_buffer[2].g = (float)vn3->g;

				v_buffer[3].x = vn4->xs;
				v_buffer[3].y = vn4->ys;
				v_buffer[3].ooz = one / vn4->zv;
				v_buffer[3].u = tex->u4 << 8;
				v_buffer[3].v = tex->v4 << 8;
				v_buffer[3].g = (float)vn4->g;
			}

			/* May need to clip resulting polygon */
			if (vn1->clip || vn2->clip || vn3->clip || vn4->clip)
				npoints = XYGUVClipper(4, v_buffer);
			else
				npoints = 4;
		}

		/* Now insert the polygon into the edge table */
		InsertEdges(npoints, tex->tpage, type);
	}
	return (objptr);
}


sint16 *InsertTexture3(sint16 *objptr, int number, int persp_dist)
{
	/* Insert triangle textures, clipping if necessary. Uses persp_dist to determine if polygons close enough to
		be drawn perspective correct or not. Allows objects to set persp_dist to enable or disable perspective
		correction. */
	/* NOTE: may want to automate persp_dist stuff be choosing whether to use perspective correction or not
		by the width of the polygon (i.e. if over 32 pixels then use it, else don't) */
	PHD_VBUF	*vn1, *vn2, *vn3;
	PHDTEXTURESTRUCT *tex;
	POINT_INFO point[4];
	int npoints, type;
	float zdepth;

	for ( ; number>0; number--, objptr+=4)
	{
		vn1 = &vbuf[objptr[0]];
		vn2 = &vbuf[objptr[1]];
		vn3 = &vbuf[objptr[2]];
		if ( vn1->clip & vn2->clip & vn3->clip )
			continue;

		if ( vn1->clip<0 || vn2->clip<0 || vn3->clip<0 )
		{
			if (!visible_zclip(vn1,vn2,vn3))
				continue;

			/* Need to Z-clip polygon first */
			tex = &phdtextinfo[objptr[3]];
			point[0].xv = vn1->xv;
			point[0].yv = vn1->yv;
			point[0].zv = vn1->zv;
			point[0].xs = vn1->xs;
			point[0].ys = vn1->ys;
			point[0].g = (float)vn1->g;
			point[0].u = (float)tex->u1;
			point[0].v = (float)tex->v1;

			point[1].xv = vn2->xv;
			point[1].yv = vn2->yv;
			point[1].zv = vn2->zv;
			point[1].xs = vn2->xs;
			point[1].ys = vn2->ys;
			point[1].g = (float)vn2->g;
			point[1].u = (float)tex->u2;
			point[1].v = (float)tex->v2;

			point[2].xv = vn3->xv;
			point[2].yv = vn3->yv;
			point[2].zv = vn3->zv;
			point[2].xs = vn3->xs;
			point[2].ys = vn3->ys;
			point[2].g = (float)vn3->g;
			point[2].u = (float)tex->u3;
			point[2].v = (float)tex->v3;

			npoints = ZedClipper(3, point, v_buffer);
			if (npoints)
				npoints = XYGUVClipper(npoints, v_buffer);

			/* z clipped polys are always perspective correct */
			type = tex->drawtype + (DRAW_POLY_GTMAP_PERSPECTIVE-DRAW_POLY_GTMAP);
		}
		else
		{
			/* Don't need Z clipping (though may still need normal clipping) */
			IF_NOT_VIS(vn1,vn2,vn3)
				continue;

			tex = &phdtextinfo[objptr[3]];

			/* Choose whether this polygon will be perspective correct or not */
			TRIA_DEPTH_FAR(vn1,vn2,vn3);

			// if whole poly closer than perspective distance? how about if any part of poly?
			if (zdepth < persp_dist) 
			{
				type = tex->drawtype + (DRAW_POLY_GTMAP_PERSPECTIVE-DRAW_POLY_GTMAP);

				v_buffer[0].x = vn1->xs;
				v_buffer[0].y = vn1->ys;
				v_buffer[0].ooz = one / vn1->zv;
				v_buffer[0].u = tex->u1 * v_buffer[0].ooz;
				v_buffer[0].v = tex->v1 * v_buffer[0].ooz;
				v_buffer[0].g = (float)vn1->g;

				v_buffer[1].x = vn2->xs;
				v_buffer[1].y = vn2->ys;
				v_buffer[1].ooz = one / vn2->zv;
				v_buffer[1].u = tex->u2 * v_buffer[1].ooz;
				v_buffer[1].v = tex->v2 * v_buffer[1].ooz;
				v_buffer[1].g = (float)vn2->g;

				v_buffer[2].x = vn3->xs;
				v_buffer[2].y = vn3->ys;
				v_buffer[2].ooz = one / vn3->zv;
				v_buffer[2].u = tex->u3 * v_buffer[2].ooz;
				v_buffer[2].v = tex->v3 * v_buffer[2].ooz;
				v_buffer[2].g = (float)vn3->g;
			}
			else
			{
				type = tex->drawtype;

				v_buffer[0].x = vn1->xs;
				v_buffer[0].y = vn1->ys;
				v_buffer[0].ooz = one / vn1->zv;
				v_buffer[0].u = tex->u1 << 8;
				v_buffer[0].v = tex->v1 << 8;
				v_buffer[0].g = (float)vn1->g;

				v_buffer[1].x = vn2->xs;
				v_buffer[1].y = vn2->ys;
				v_buffer[1].ooz = one / vn2->zv;
				v_buffer[1].u = tex->u2 << 8;
				v_buffer[1].v = tex->v2 << 8;
				v_buffer[1].g = (float)vn2->g;

				v_buffer[2].x = vn3->xs;
				v_buffer[2].y = vn3->ys;
				v_buffer[2].ooz = one / vn3->zv;
				v_buffer[2].u = tex->u3 << 8;
				v_buffer[2].v = tex->v3 << 8;
				v_buffer[2].g = (float)vn3->g;
			}

			/* May need to clip resulting polygon */
			if (vn1->clip || vn2->clip || vn3->clip)
				npoints = XYGUVClipper(3, v_buffer);
			else
				npoints = 3;
		}

		/* Now insert the polygon into the edge table */
		InsertEdges(npoints, tex->tpage, type);
	}
	return (objptr);
}


sint16 *InsertGouraud4(sint16 *objptr, int number)
{
	/* Insert quad gourauds */
	int npoints;
	PHD_VBUF	*vn1, *vn2, *vn3, *vn4;

	for ( ; number>0; number--, objptr+=5)
	{
		vn1 = &vbuf[objptr[0]];
		vn2 = &vbuf[objptr[1]];
		vn3 = &vbuf[objptr[2]];
		vn4 = &vbuf[objptr[3]];
		if ( vn1->clip & vn2->clip & vn3->clip & vn4->clip )
			continue;

		/* No z clipping of gourauds (wasn't done in original game) */
		if ( vn1->clip<0 || vn2->clip<0 || vn3->clip<0 || vn4->clip<0 )
			continue;

		IF_NOT_VIS(vn1,vn2,vn3)
			continue;

		v_buffer[0].x = vn1->xs;
		v_buffer[0].y = vn1->ys;
		v_buffer[0].ooz = one / vn1->zv;
		v_buffer[0].g = (float)vn1->g;

		v_buffer[1].x = vn2->xs;
		v_buffer[1].y = vn2->ys;
		v_buffer[1].ooz = one / vn2->zv;
		v_buffer[1].g = (float)vn2->g;

		v_buffer[2].x = vn3->xs;
		v_buffer[2].y = vn3->ys;
		v_buffer[2].ooz = one / vn3->zv;
		v_buffer[2].g = (float)vn3->g;

		v_buffer[3].x = vn4->xs;
		v_buffer[3].y = vn4->ys;
		v_buffer[3].ooz = one / vn4->zv;
		v_buffer[3].g = (float)vn4->g;

		/* May need to clip resulting polygon */
		if (vn1->clip || vn2->clip || vn3->clip || vn4->clip)
			npoints = XYGClipper(4, v_buffer);
		else
			npoints = 4;

		/* Now insert the polygon into the edge table */
		InsertEdges(npoints, objptr[4], DRAW_POLY_GOURAUD);
	}

	return (objptr);
}


sint16 *InsertGouraud3(sint16 *objptr, int number)
{
	/* Insert quad gourauds */
	int npoints;
	PHD_VBUF	*vn1, *vn2, *vn3;

	for ( ; number>0; number--, objptr+=4)
	{
		vn1 = &vbuf[objptr[0]];
		vn2 = &vbuf[objptr[1]];
		vn3 = &vbuf[objptr[2]];
		if ( vn1->clip & vn2->clip & vn3->clip )
			continue;

		/* No z clipping of gourauds (wasn't done in original game) */
		if ( vn1->clip<0 || vn2->clip<0 || vn3->clip<0 )
			continue;

		IF_NOT_VIS(vn1,vn2,vn3)
			continue;

		v_buffer[0].x = vn1->xs;
		v_buffer[0].y = vn1->ys;
		v_buffer[0].ooz = one / vn1->zv;
		v_buffer[0].g = (float)vn1->g;

		v_buffer[1].x = vn2->xs;
		v_buffer[1].y = vn2->ys;
		v_buffer[1].ooz = one / vn2->zv;
		v_buffer[1].g = (float)vn2->g;

		v_buffer[2].x = vn3->xs;
		v_buffer[2].y = vn3->ys;
		v_buffer[2].ooz = one / vn3->zv;
		v_buffer[2].g = (float)vn3->g;

		/* May need to clip resulting polygon */
		if (vn1->clip || vn2->clip || vn3->clip )
			npoints = XYGClipper(3, v_buffer);
		else
			npoints = 3;

		/* Now insert the polygon into the edge table */
		InsertEdges(npoints, objptr[3], DRAW_POLY_GOURAUD);
	}

	return (objptr);
}


void RenderSpanGT32(SPAN *span, unsigned char *lineptr)
{
	/* This version does 32 pixels at a time linearly, and adaptively double pixels for each 32 pixel span.
		Should only be used in hires though */
	unsigned char *pixptr, *tptr;
	float ooz, uoz, voz, scale;
	float oozadd32, uozadd32, vozadd32;
	int	start_g, add_g, add_g2;
	int	end_u, end_v;
	int	start_u, start_v;
	int	add_u, add_v;
	int	x;
	EDGE *start_edge, *end_edge;

	/* Special case spans of 32 pixels or less, as no perspective correction needs doing */
	if (span->end_x - span->start_x <= 32)
	{
#ifdef OLD
		start_edge = span->start_edge;
		end_edge = span->end_edge;

		span->start_x = start_edge->x>>16;

		x = (end_edge->x>>16) - span->start_x;
		start_g = start_edge->g;
		add_g = (end_edge->g - start_g) / x;

		scale = 1.0f/start_edge->ooz;
		start_u = (int)(start_edge->uoz * scale);
		start_v = (int)(start_edge->voz * scale);

		scale = 1.0f/end_edge->ooz;
		end_u = (int)(end_edge->uoz * scale);
		add_u = (end_u - start_u) / x;

		end_v = (int)(end_edge->voz * scale);
		add_v = (end_v - start_v) / x;

		tptr = texture_page_ptrs[start_edge->colour];

		pixptr = lineptr + span->start_x;
#endif

		start_edge = span->start_edge;

		scale = 1.0f/start_edge->ooz;

		span->start_x = start_edge->x>>16;
		start_g = start_edge->g;
		
		start_u = (int)(start_edge->uoz * scale);
		start_v = (int)(start_edge->voz * scale);


		end_edge = span->end_edge;

		scale = 1.0f/end_edge->ooz;

		tptr = texture_page_ptrs[start_edge->colour];
		pixptr = lineptr + span->start_x;

		end_u = (int)(end_edge->uoz * scale);
		end_v = (int)(end_edge->voz * scale);

		scale = 65536.0f / (end_edge->x - start_edge->x);

		add_g = (end_edge->g - start_g) * scale;
		add_u = (end_u - start_u) * scale;
		add_v = (end_v - start_v) * scale;

		if ((ABS(add_u)+ABS(add_v)) & SINGLE_MASK)
		{
			/* Single pixel it */
			for (x=span->end_x - span->start_x; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
				pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}
		}
		else
		{
			/* Double pixel it */
			add_u <<= 1;
			add_v <<= 1;
			add_g <<= 1;

			for (x=span->end_x - span->start_x; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}
		}

		if (x)
			pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16)) ];

		return;
	}


	/* Render spans of over 32 pixels in linear strips of 32 pixels */
#ifdef OLD
	start_edge = span->start_edge;
	end_edge = span->end_edge;

	span->start_x = start_edge->x>>16;

	x = (end_edge->x>>16) - span->start_x;
	start_g = start_edge->g;
	add_g = (end_edge->g - start_g) / x;
	add_g2 = add_g<<1;

	scale = 32.0f/x;
	ooz = start_edge->ooz;
	oozadd32 = (end_edge->ooz - ooz) * scale;
	uoz = start_edge->uoz;
	uozadd32 = (end_edge->uoz - uoz) * scale;
	voz = start_edge->voz;
	vozadd32 = (end_edge->voz - voz) * scale;

	scale = 1.0f/ooz;
	end_u = (int)(uoz * scale);
	end_v = (int)(voz * scale);

	// better to convert this and store this once when edge inserted... (store colour as long and cast here)
	tptr = texture_page_ptrs[start_edge->colour];

	pixptr = lineptr + span->start_x;

	ooz += oozadd32;
	scale = 1.0f/ooz;

	for (x=span->end_x-span->start_x; x>=32; x-=32)
#endif

	start_edge = span->start_edge;
	end_edge = span->end_edge;

	scale = 65536.0f / (end_edge->x - start_edge->x);

	span->start_x = start_edge->x>>16;

	x = span->end_x - span->start_x;

	start_g = start_edge->g;
	add_g = (end_edge->g - start_g) * scale;
	add_g2 = add_g<<1;

	scale *= 32.0f;
	ooz = start_edge->ooz;
	oozadd32 = (end_edge->ooz - ooz) * scale;
	uoz = start_edge->uoz;
	uozadd32 = (end_edge->uoz - uoz) * scale;
	voz = start_edge->voz;
	vozadd32 = (end_edge->voz - voz) * scale;

	scale = 1.0f/ooz;
	end_u = (int)(uoz * scale);
	end_v = (int)(voz * scale);

	tptr = texture_page_ptrs[start_edge->colour];

	pixptr = lineptr + span->start_x;

	ooz += oozadd32;
	scale = 1.0f/ooz;

	for ( ; x>=32; x-=32)
	{
		start_u = end_u;
		uoz += uozadd32;
		end_u = (int)(uoz*scale);
		add_u = (end_u-start_u)>>5;

		start_v = end_v;
		voz += vozadd32;
		end_v = (int)(voz*scale);
		add_v = (end_v-start_v)>>5;

		if ((ABS(add_u)+ABS(add_v)) & SINGLE_MASK)
		{
			// calc here so FP divide is in parallel with integer stuff
			ooz += oozadd32;
			scale = 1.0f/ooz;

			/* Single pixel it */
			*(pixptr) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+1) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+2) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+3) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+4) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+5) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+6) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+7) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+8) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+9) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+10) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+11) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+12) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+13) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+14) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+15) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_g += add_g;
			start_u += add_u;
			start_v += add_v;

			*(pixptr+16) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+17) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+18) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+19) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+20) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+21) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+22) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+23) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+24) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+25) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+26) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+27) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+28) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+29) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+30) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+31) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_g += add_g;
		}
		else
		{
			/* Double pixel it */
			add_u <<= 1;
			add_v <<= 1;

			ooz += oozadd32;
			scale = 1.0f/ooz;

			*(pixptr) = *(pixptr+1) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+2) = *(pixptr+3) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+4) = *(pixptr+5) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+6) = *(pixptr+7) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+8) = *(pixptr+9) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+10) = *(pixptr+11) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+12) = *(pixptr+13) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+14) = *(pixptr+15) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+16) = *(pixptr+17) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+18) = *(pixptr+19) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+20) = *(pixptr+21) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+22) = *(pixptr+23) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+24) = *(pixptr+25) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+26) = *(pixptr+27) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+28) = *(pixptr+29) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+30) = *(pixptr+31) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16) ) ];
			start_g += add_g2;
		}

		pixptr += 32;
	}

	if (x)
	{
		// how far to end of span? x + ((end_edge->x>>16) - span->end_x)
		scale = 1.0f/end_edge->ooz;

		start_u = end_u;
		end_u = (int)(end_edge->uoz * scale);
		start_v = end_v;
		end_v = (int)(end_edge->voz * scale);

		scale = 1.0f / (x + ((end_edge->x>>16) - span->end_x));
		add_u = (end_u - start_u) * scale;
		add_v = (end_v - start_v) * scale;

		if ((ABS(add_u)+ABS(add_v)) & SINGLE_MASK)
		{
			/* Single pixel it */
			for ( ; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
				pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}
		}
		else
		{
			/* Double pixel it */
			add_u <<= 1;
			add_v <<= 1;

			for ( ; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g2;
			}
		}

		if (x)
			pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
	}
}


void RenderSpanGT16(SPAN *span, unsigned char *lineptr)
{
	/* This version does 16 pixels at a time linearly, and adaptively double pixels for each 16 pixel span.
		Should only be used in hires though */
	unsigned char *pixptr, *tptr;
	float ooz, uoz, voz, scale;
	float oozadd16, uozadd16, vozadd16;
	int	start_g, add_g, add_g2;
	int	end_u, end_v;
	int	start_u, start_v;
	int	add_u, add_v;
	int	x;
	EDGE *start_edge, *end_edge;

	/* Special case spans of 32 pixels or less, as no perspective correction needs doing */
	if (span->end_x - span->start_x <= 16)
	{
		start_edge = span->start_edge;

		scale = 1.0f/start_edge->ooz;

		span->start_x = start_edge->x>>16;
		start_g = start_edge->g;
		
		start_u = (int)(start_edge->uoz * scale);
		start_v = (int)(start_edge->voz * scale);


		end_edge = span->end_edge;

		scale = 1.0f/end_edge->ooz;

		tptr = texture_page_ptrs[start_edge->colour];
		pixptr = lineptr + span->start_x;

		end_u = (int)(end_edge->uoz * scale);
		end_v = (int)(end_edge->voz * scale);

		scale = 65536.0f / (end_edge->x - start_edge->x);

		add_g = (end_edge->g - start_g) * scale;
		add_u = (end_u - start_u) * scale;
		add_v = (end_v - start_v) * scale;

		if ((ABS(add_u)+ABS(add_v)) & SINGLE_MASK)
		{
			/* Single pixel it */
			for (x=span->end_x - span->start_x; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
				pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}
		}
		else
		{
			/* Double pixel it */
			add_u <<= 1;
			add_v <<= 1;
			add_g <<= 1;

			for (x=span->end_x - span->start_x; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}
		}

		if (x)
			pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16)) ];

		return;
	}


	/* Render spans of over 32 pixels in linear strips of 32 pixels */
	start_edge = span->start_edge;
	end_edge = span->end_edge;

	scale = 65536.0f / (end_edge->x - start_edge->x);

	x = span->end_x - span->start_x;

	span->start_x = start_edge->x>>16;

	start_g = start_edge->g;
	add_g = (end_edge->g - start_g) * scale;
	add_g2 = add_g<<1;

	scale *= 16.0f;
	ooz = start_edge->ooz;
	oozadd16 = (end_edge->ooz - ooz) * scale;
	uoz = start_edge->uoz;
	uozadd16 = (end_edge->uoz - uoz) * scale;
	voz = start_edge->voz;
	vozadd16 = (end_edge->voz - voz) * scale;

	scale = 1.0f/ooz;
	end_u = (int)(uoz * scale);
	end_v = (int)(voz * scale);

	tptr = texture_page_ptrs[start_edge->colour];

	pixptr = lineptr + span->start_x;

	ooz += oozadd16;
	scale = 1.0f/ooz;

	for ( ; x>=16; x-=16)
	{
		start_u = end_u;
		uoz += uozadd16;
		end_u = (int)(uoz*scale);
		add_u = (end_u-start_u)>>4;

		start_v = end_v;
		voz += vozadd16;
		end_v = (int)(voz*scale);
		add_v = (end_v-start_v)>>4;

		if ((ABS(add_u)+ABS(add_v)) & SINGLE_MASK)
		{
			// calc here so FP divide takes place as pixels are drawn
			ooz += oozadd16;
			scale = 1.0f/ooz;

			/* Single pixel it */
			*(pixptr) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+1) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+2) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+3) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+4) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+5) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+6) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+7) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+8) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+9) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+10) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+11) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			*(pixptr+12) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+13) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+14) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			*(pixptr+15) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_g += add_g;
		}
		else
		{
			/* Double pixel it */
			add_u <<= 1;
			add_v <<= 1;

			ooz += oozadd16;
			scale = 1.0f/ooz;

			*(pixptr) = *(pixptr+1) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+2) = *(pixptr+3) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+4) = *(pixptr+5) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+6) = *(pixptr+7) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+8) = *(pixptr+9) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+10) = *(pixptr+11) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;

			*(pixptr+12) = *(pixptr+13) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g2;
			*(pixptr+14) = *(pixptr+15) = depthq_table[(start_g>>16)][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
			start_g += add_g2;
		}

		pixptr += 16;
	}

	if (x)
	{
		// how far to end of span? x + ((end_edge->x>>16) - span->end_x)
		scale = 1.0f/end_edge->ooz;

		start_u = end_u;
		end_u = (int)(end_edge->uoz * scale);
		start_v = end_v;
		end_v = (int)(end_edge->voz * scale);

		scale = 1.0f / (x + ((end_edge->x>>16) - span->end_x));
		add_u = (end_u - start_u) * scale;
		add_v = (end_v - start_v) * scale;

		if ((ABS(add_u)+ABS(add_v)) & SINGLE_MASK)
		{
			/* Single pixel it */
			for ( ; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
				pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}
		}
		else
		{
			/* Double pixel it */
			add_u <<= 1;
			add_v <<= 1;

			for ( ; x>1; x-=2, pixptr+=2)
			{
				pixptr[0] = pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g2;
			}
		}

		if (x)
			pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
	}
}


void RenderSpanWGT16(SPAN *span, unsigned char *lineptr)
{
	unsigned char *pixptr, *tptr, texel;
	float ooz, uoz, voz, scale;
	float oozadd16, uozadd16, vozadd16;
	int	start_g, add_g;
	int	end_u, end_v;
	int	start_u, start_v;
	int	add_u, add_v;
	int	x, end_bit;
	EDGE *start_edge, *end_edge;

	if (span->end_x - span->start_x > 0)
	{
		start_edge = span->start_edge;
		end_edge = span->end_edge;

		span->start_x = start_edge->x>>16;

		x = (end_edge->x>>16) - span->start_x;
		start_g = start_edge->g;
		add_g = (end_edge->g - start_g) / x;

		scale = 16.0f/x;
		ooz = start_edge->ooz;
		oozadd16 = (end_edge->ooz - ooz) * scale;
		uoz = start_edge->uoz;
		uozadd16 = (end_edge->uoz - uoz) * scale;
		voz = start_edge->voz;
		vozadd16 = (end_edge->voz - voz) * scale;

		scale = 1.0f/ooz;
		end_u = (int)(uoz * scale);
		end_v = (int)(voz * scale);

		// better to convert this and store this once when edge inserted... (store colour as long and cast here)
		tptr = texture_page_ptrs[start_edge->colour];

		pixptr = lineptr + span->start_x;
		for (x=span->end_x-span->start_x; x>=16; x-=16)
		{
			ooz += oozadd16;
			scale = 1.0f/ooz;

			start_u = end_u;
			uoz += uozadd16;
			end_u = (int)(uoz*scale);
			add_u = (end_u-start_u)>>4;

			start_v = end_v;
			voz += vozadd16;
			end_v = (int)(voz*scale);
			add_v = (end_v-start_v)>>4;

			/* Single pixel it */
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+1) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+2) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+3) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+4) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+5) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+6) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+7) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+8) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+9) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+10) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+11) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;

			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+12) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+13) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+14) = depthq_table[(start_g>>16)][texel];
			start_u += add_u;
			start_v += add_v;
			start_g += add_g;
			if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
				*(pixptr+15) = depthq_table[(start_g>>16)][texel];
			start_g += add_g;

			pixptr += 16;
		}

		if (x)
		{
			// how far to end of span? x + ((end_edge->x>>16) - span->end_x)
			scale = 1.0f/end_edge->ooz;
			end_bit = x + ((end_edge->x>>16) - span->end_x);

			start_u = end_u;
			end_u = (int)(end_edge->uoz * scale);
			add_u = (end_u - start_u) / end_bit;

			start_v = end_v;
			end_v = (int)(end_edge->voz * scale);
			add_v = (end_v - start_v) / end_bit;
	
			for ( ; x>1; x-=2, pixptr+=2)
			{
				if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 	
					*pixptr = depthq_table[start_g>>16][texel];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
				if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 	
					*(pixptr+1) = depthq_table[start_g>>16][texel];
				start_u += add_u;
				start_v += add_v;
				start_g += add_g;
			}

			if (x && (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) )
				pixptr[0] = depthq_table[start_g>>16][texel];
		}
	}
}


void RenderSpanGTnp(SPAN *span, unsigned char *lineptr)
{
	/* Enormously slow renderer for non-perspective correct textures */
	int x, start_g, add_g;
	int start_u, start_v, add_u, add_v;
	unsigned char *pixptr, *tptr;
	EDGE *start_edge, *end_edge;
	float scale;

#ifdef OLD
	start_edge = span->start_edge;
	end_edge = span->end_edge;

	span->start_x = start_edge->x>>16; // makes span drawn from very start rather than actual start (i.e not clipped)
	x = (end_edge->x>>16) - span->start_x;
	scale = 1.0f/x;

	start_g = start_edge->g;
	add_g = (end_edge->g - start_g)/x;

	start_u = (int)start_edge->uoz;
	add_u = ((int)end_edge->uoz - start_u) * scale;

	start_v = (int)start_edge->voz;
	add_v = ((int)end_edge->voz - start_v) * scale;

	tptr = texture_page_ptrs[start_edge->colour];
	pixptr = lineptr + span->start_x;
#endif
	start_edge = span->start_edge;
	end_edge = span->end_edge;

	scale = 65536.0f/(end_edge->x - start_edge->x);

	span->start_x = start_edge->x>>16; // makes span drawn from very start rather than actual start (i.e not clipped)
	x = span->end_x - span->start_x;

	tptr = texture_page_ptrs[start_edge->colour];
	pixptr = lineptr + span->start_x;

	start_g = start_edge->g;
	add_g = (end_edge->g - start_g) * scale;

	start_u = (int)start_edge->uoz;
	add_u = (end_edge->uoz - start_edge->uoz) * scale;

	start_v = (int)start_edge->voz;
	add_v = (end_edge->voz - start_edge->voz) * scale;

	// Double pixelling doesn't seem to make any odds, so only single pixel
	// 16 pixels at a time gets the gain, 32 isn't any improvement
	for ( ; x>15; x-=16, pixptr+=16)
	{
		pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[2] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[3] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[4] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[5] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[6] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[7] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[8] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[9] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[10] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[11] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[12] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[13] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[14] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[15] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
	}

	for ( ; x>1; x-=2, pixptr+=2)
	{
		pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		pixptr[1] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
	}

	if (x)
		pixptr[0] = depthq_table[start_g>>16][*(tptr + ((start_v>>16)<<8) + (start_u>>16))];
}


void RenderSpanWGTnp(SPAN *span, unsigned char *lineptr)
{
	/* Enormously slow renderer for non-perspective correct textures WINDOWED textures */
	int x, start_g, add_g;
	int start_u, start_v, add_u, add_v;
	unsigned char *pixptr, *tptr, texel;
	EDGE *start_edge, *end_edge;
	float scale;

	start_edge = span->start_edge;
	end_edge = span->end_edge;

	scale = 65536.0f/(end_edge->x - start_edge->x);

	span->start_x = start_edge->x>>16; // makes span drawn from very start rather than actual start (i.e not clipped)
	x = span->end_x - span->start_x;

	tptr = texture_page_ptrs[start_edge->colour];
	pixptr = lineptr + span->start_x;

	start_g = start_edge->g;
	add_g = (end_edge->g - start_g) * scale;

	start_u = (int)start_edge->uoz;
	add_u = (end_edge->uoz - start_edge->uoz) * scale;

	start_v = (int)start_edge->voz;
	add_v = (end_edge->voz - start_edge->voz) * scale;

	// Double pixelling doesn't seem to make any odds, so only single pixel
	// 16 pixels at a time gets the gain, 32 isn't any improvement
	for ( ; x>15; x-=16, pixptr+=16)
	{
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[0] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[1] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[2] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[3] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[4] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[5] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[6] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[7] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[8] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[9] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[10] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[11] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[12] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[13] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[14] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[15] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;

	}

	for ( ; x>1; x-=2, pixptr+=2)
	{
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[0] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
		if ( (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
			pixptr[1] = depthq_table[(start_g>>16)][texel];
		start_u += add_u;
		start_v += add_v;
		start_g += add_g;
	}

	if (x && (texel = *(tptr + ((start_v>>16)<<8) + (start_u>>16))) ) 
		pixptr[0] = depthq_table[(start_g>>16)][texel];
}


void RenderSpanG(SPAN *span, unsigned char *lineptr)
{
	int x, start_g, add_g;
	unsigned char *pixptr, *gouraud;
	EDGE *start_edge, *end_edge;

	start_edge = span->start_edge;
	end_edge = span->end_edge;

	x = (end_edge->x>>16) - span->start_x;
	start_g = start_edge->g;
	add_g = (end_edge->g - start_g) / x;

	gouraud = gouraud_table[start_edge->colour];

	pixptr = lineptr + span->start_x;

	/* Single pixel it */
	for (x=span->end_x - span->start_x; x>1; x-=2, pixptr+=2)
	{
		pixptr[0] = gouraud[start_g>>16];
		start_g += add_g;
		pixptr[1] = gouraud[start_g>>16];
		start_g += add_g;
	}

	if (x)
		pixptr[0] = gouraud[start_g>>16];
}


void RenderBackground(SPAN *span, unsigned char *lineptr)
{
//	memset(lineptr + span->start_x, 30, span->end_x - span->start_x);
}


void (*RenderSpan[])(SPAN *span, unsigned char *lineptr) = {
	RenderSpanGTnp, RenderSpanWGTnp, RenderSpanGT32, RenderSpanWGT16, RenderBackground, RenderBackground, RenderSpanG
//	RenderSpanG, RenderSpanWGTnp, RenderSpanGT32, RenderSpanWGT16, RenderBackground, RenderBackground, RenderSpanG
};


void Stage1(EDGE *aet, sint32 y)
{
	EDGE *edge, *prev, *next;

	edge = edge_table[y];
	while (edge)
	{
		/* Go through new edges sorting them to correct position on list */
		edge->link = NULL;
		prev = aet;
		while (edge->x > prev->next->x) // this will always terminate because of 'aet_end'
		{
			prev = prev->next;

			/* Look for matching edge */
			if (edge->id == prev->id)
			{
				edge->link = prev;
				prev->link = edge;
			}
		}

		/* Remember next edge in list of ones to be added this on this line */
		next = edge->next;

		/* Place edge in appropriate place on list */
		edge->prev = prev;
		edge->next = prev->next;
		edge->next->prev = edge;
		prev->next = edge;

		/* Continue search for matching edge if not yet located */
		if (!edge->link)
		{
			prev = edge->next;
			while (prev)
			{
				if (edge->id == prev->id)
				{
					edge->link = prev;
					prev->link = edge;
					break;
				}
				prev = prev->next;
			}
		}

		/* Move onto next edge to be added */
		edge = next;
	}

	/* Clear this edge table entry for the next time around */
	edge_table[y] = NULL;
}


void Stage2(EDGE *aet, EDGE *aet_end, SPAN *active, SPAN *draw)
{
	EDGE *edge;
	SPAN *span, *next_span, *prev_span;

	span = spare_span;
	spare_span = span->next;

	span->start_x = 0;
	span->start_edge = aet;
	span->end_edge = aet_end;
	span->ooz = 0.0f;
	span->ooz_add = 0.0f;
	span->next = NULL;
	span->draw_next = NULL;
	span->draw_prev = draw;

	draw->next = span;
	active->next = span;

	// bound this with a check for aet.next != aet_end; then only add edges up to before edge->next==NULL
	for (edge=aet->next; edge!=NULL; edge=edge->next)
	{
		/* Can skip spans that will be zero length */
		if ((edge->x & 0xffff0000) == (edge->link->x & 0xffff0000))
			continue;

		if (edge->x < edge->link->x)
		{
			/* Leading edge: Start new active span */
			span = spare_span;
			if (span == NULL)
				S_ExitSystem("No spare spans");
			spare_span = span->next;

			span->start_x = span->end_x = NOT_VISIBLE;
			span->start_edge = edge;
			span->end_edge = edge->link;
			span->ooz = edge->ooz;
			span->ooz_add = (edge->link->ooz - edge->ooz) / (edge->link->x - edge->x);
				
			/* Find position in active list: this WILL be bounded by BACKGROUND span */
			prev_span = active;
			next_span = prev_span->next;
			while (span->ooz < next_span->ooz + next_span->ooz_add * (edge->x - next_span->start_edge->x))
			{
				prev_span = next_span;
				next_span = prev_span->next;
			}

			/* Add in that position */
			span->next = next_span;
			prev_span->next = span;

			/* Is it at top of active list? */
			if (prev_span == active)
			{
				/* Know it is visible */
				span->start_x = edge->x >> 16;

				/* Place BELOW (on the draw list) the span that is currently top on the active list (next_span); this ensures;
						a) if span below has been cut, it will get drawn first (in correct x order)
						b) if this span is transparent, span below will get draw first
					i.e. it's always the best choice */
				span->draw_prev = next_span;
				span->draw_next = next_span->draw_next;
				if (span->draw_next)
					span->draw_next->draw_prev = span;
				next_span->draw_next = span;
			}
			else
			{
				/* Add to draw list ABOVE prev_span */
				span->draw_next = prev_span;
				span->draw_prev = prev_span->draw_prev;
				span->draw_prev->draw_next = span;
				prev_span->draw_prev = span;

				/* Span may not be top of active list, but may still be visible if one above is a) visible b) transparent */
				if (prev_span->start_x == NOT_VISIBLE || !(prev_span->start_edge->type & TRANSPARENT_TYPE))
					continue;

				span->start_x = edge->x >> 16;
			}

			/* If new span is visible, but opaque, need to cut all visible spans beneath it */
			if (span->start_edge->type & TRANSPARENT_TYPE)
				continue;

			do
			{
				span = span->next;

				/* Mark end of span on draw list */
				if (span->end_x == NOT_VISIBLE)
					span->end_x = edge->x >> 16;
			}
			while (span->start_edge->type & TRANSPARENT_TYPE);
		}
		else
		{
			/* Trailing edge: must have matching span already on list, else there's been an error */
			prev_span = active;
			while (prev_span->next->end_edge != edge)
				prev_span = prev_span->next;

			/* Got span; now remove it from active list */
			span = prev_span->next;
			prev_span->next = span->next;

			/* If span wasn't visible, then place on spare list (saved drawing it altogether) */
			if (span->start_x == NOT_VISIBLE)
			{
				/* Remove from draw list */
				span->draw_prev->draw_next = span->draw_next;
				span->draw_next->draw_prev = span->draw_prev;

				span->next = spare_span;
				spare_span = span;
			}
			else if (span->end_x == NOT_VISIBLE)
			{
				/* Else needs completing on draw list */
				span->end_x = edge->x >> 16;

				// aet_end shouldn't really be in here; this test is purely for it
				if (span->next == NULL)
					continue;

				/* If it was opaque, then span below is now visible, so needs making visible */
				if (span->start_edge->type & TRANSPARENT_TYPE)
					continue;

				/*	Keep making spans visible until an opaque one is encountered */
				do
				{
					span = span->next;

					/* NOTE: May be visible already because only temporarily obscured */
					if (span->start_x == NOT_VISIBLE)
						span->start_x = edge->x >> 16; // start span for first time
					else
						span->end_x = NOT_VISIBLE; // restart span (by removing end mark)
				}
				while (span->start_edge->type & TRANSPARENT_TYPE);
			}
		}
	}
}


void Stage3(SPAN *draw, char *lineptr)
{
	SPAN *span;

	span = draw;
	while (span)
	{
		if (span->end_x - span->start_x > 0)
			(*RenderSpan[span->start_edge->type])(span, lineptr);

		/* Finished with span, so place back on spare list */
		span->next = spare_span;
		spare_span = span;

		span = span->draw_next;
	}
}


void Stage4(EDGE *aet)
{
	EDGE *edge, *next, *prev;

	edge = aet;
	while (edge->next)
	{
		next = edge->next;

		/* First check that edge hasn't reached its sell by date */
		if (--edge->y)
		{
			edge->ooz += edge->ooz_add;
			edge->x += edge->x_add;
			edge->uoz += edge->uoz_add;
			edge->g += edge->g_add;
			edge->voz += edge->voz_add;

			/* May have swapped position with things that preceed it in edge table */
			prev = edge->prev;

			if (prev->x > edge->x)
			{
				do
					prev = prev->prev;
				while (prev->x > edge->x);

				/* Remove from old position */
				edge->prev->next = edge->next;
				edge->next->prev = edge->prev;

				/* Add to new */
				edge->prev = prev;
				edge->next = prev->next;
				edge->next->prev = edge;
				prev->next = edge;
			}
		}
		else
		{
			/* Edge has expired, remove from AET and add to spare list again */
			edge->next->prev = edge->prev; // or  next->prev = edge->prev;
			edge->prev->next = edge->next; // and next->prev->next = next;

			edge->next = spare_edge;
			spare_edge = edge;
		}

		edge = next;
	}
}


void DrawDisplay(void)
{
	/* THIS VERSION DOES NOT BREAK SPANS INTO FRAGMENTS IF FOREGROUND SPAN OBSCURES A PORTION (E.G. LARA STANDING
		IN FRONT OF A WALL TEXTURE). THE FRAGMENTS CAUSED PROBLEMS BECAUSE THEY NEEDED u,v,g TO BE CLIPPED FOR
		START OF SPAN, AND COULD NOT EASILY DO THIS ACCURATELY ENOUGH TO STOP PIXELS ON FRAGMENT JUDDERING ABOUT AS
		START CLIP x POS MOVED BETWEEN FRAMES (AS HAPPENS AS LARA 'BREATHES').

		INSTEAD, SPANS ARE PLACED ON THE DRAW LIST AUTOMATICALLY AS LEADING EDGE IS FOUND, AND REMOVED IT NOT_VISIBLE
		WHEN TRAILING EDGE FOUND. START x IS ALWAYS THE START OF THE SPAN, BUT END EDGE IS CLIPPED. */
	EDGE aet, aet_end;
	SPAN active, draw;
	sint32 y;
	char *lineptr;

	/* Setup active edge start and ends */
	aet.prev = NULL;
	aet.next = &aet_end;
	aet.id = 0;
	aet.type = BACKGROUND_TYPE;
	aet.x = 0;
	aet.x_add = 0;
	aet.y = phd_scrheight + 10; // longer than screen so routine doesn't try to remove these edges ever
	aet.ooz = 0.0f;
	aet.ooz_add = 0.0f;
	aet.link = &aet_end;

	aet_end.prev = &aet;
	aet_end.next = NULL;
	aet_end.id = 0;
	aet_end.type = BACKGROUND_TYPE;
	aet_end.x = (phd_scrwidth<<16) + 0xffff;
	aet_end.x_add = 0;
	aet_end.y = aet.y;
	aet_end.ooz = 0.0f;
	aet_end.ooz_add = 0.0f;
	aet_end.link = &aet;

	lineptr = phd_winptr;
	for (y=0; y<phd_scrheight; y++)
	{
		/* STAGE 1: add edges that start on this line to AET. In addition, find matching edge (if exists; may be
			being inserted too) */
		Stage1(&aet, y);

		/* STAGE 2: Go through AET adding and removing edges from draw list, and adding spans to span list */
		Stage2(&aet, &aet_end, &active, &draw);

		/* STAGE 3: Render span list */
		Stage3(draw.next, lineptr);

		lineptr += phd_scrwidth;

		/* STAGE 4: Move and remove active edges*/
		Stage4(aet.next);
	}
}


#ifdef OLD_ONE
extern char exit_message[];
void DrawDisplay(void)
{
	/* THIS VERSION DOES NOT BREAK SPANS INTO FRAGMENTS IF FOREGROUND SPAN OBSCURES A PORTION (E.G. LARA STANDING
		IN FRONT OF A WALL TEXTURE). THE FRAGMENTS CAUSED PROBLEMS BECAUSE THEY NEEDED u,v,g TO BE CLIPPED FOR
		START OF SPAN, AND COULD NOT EASILY DO THIS ACCURATELY ENOUGH TO STOP PIXELS ON FRAGMENT JUDDERING ABOUT AS
		START CLIP x POS MOVED BETWEEN FRAMES (AS HAPPENS AS LARA 'BREATHES').

		INSTEAD, SPANS ARE PLACED ON THE DRAW LIST AUTOMATICALLY AS LEADING EDGE IS FOUND, AND REMOVED IT NOT_VISIBLE
		WHEN TRAILING EDGE FOUND. START x IS ALWAYS THE START OF THE SPAN, BUT END EDGE IS CLIPPED. */
	EDGE *edge, *prev, *next;
	EDGE aet, aet_end;
	SPAN *span, *prev_span, *next_span;
	SPAN active, draw;
	sint32 y;
	char *lineptr;

	/* Setup active edge start and ends */
	aet.prev = NULL;
	aet.next = &aet_end;
	aet.id = 0;
	aet.type = BACKGROUND_TYPE;
	aet.x = 0;
	aet.x_add = 0;
	aet.y = phd_scrheight + 10; // longer than screen so routine doesn't try to remove these edges ever
	aet.ooz = 0.0f;
	aet.ooz_add = 0.0f;
	aet.link = &aet_end;

	aet_end.prev = &aet;
	aet_end.next = NULL;
	aet_end.id = 0;
	aet_end.type = BACKGROUND_TYPE;
	aet_end.x = (phd_scrwidth<<16) + 0xffff;
	aet_end.x_add = 0;
	aet_end.y = aet.y;
	aet_end.ooz = 0.0f;
	aet_end.ooz_add = 0.0f;
	aet_end.link = &aet;

	lineptr = phd_winptr;
	for (y=0; y<phd_scrheight; y++)
	{
		/* STAGE 1: add edges that start on this line to AET. In addition, find matching edge (if exists; may be
			being inserted too) */
		edge = edge_table[y];
		while (edge)
		{
			/* Go through new edges sorting them to correct position on list */
			edge->link = NULL;
			prev = &aet;
			while (edge->x > prev->next->x) // this will always terminate because of 'aet_end'
			{
				prev = prev->next;
#ifdef DEBUG_SPAN
if (prev == NULL)
	S_ExitSystem("Insert edge flunk");
#endif

				/* Look for matching edge */
				if (edge->id == prev->id)
				{
					edge->link = prev;
					prev->link = edge;
				}
			}

			/* Remember next edge in list of ones to be added this on this line */
			next = edge->next;

			/* Place edge in appropriate place on list */
			edge->prev = prev;
			edge->next = prev->next;
			edge->next->prev = edge;
			prev->next = edge;

			/* Continue search for matching edge if not yet located */
			if (!edge->link)
			{
				prev = edge->next;
				while (prev)
				{
					if (edge->id == prev->id)
					{
						edge->link = prev;
						prev->link = edge;
						break;
					}
					prev = prev->next;
				}
			}

			/* Move onto next edge to be added */
			edge = next;
		}

		/* Clear this edge table entry for the next time around */
		edge_table[y] = NULL;


		/* STAGE 2: Go through AET adding and removing edges from draw list, and adding spans to span list */
		span = spare_span;
		spare_span = span->next;

		span->start_x = 0;
		span->start_edge = &aet;
		span->end_edge = &aet_end;
		span->ooz = 0.0f;
		span->ooz_add = 0.0f;
		span->next = NULL;
		span->draw_next = NULL;
		span->draw_prev = &draw;

		draw.next = span;
		active.next = span;

		// bound this with a check for aet.next != aet_end; then only add edges up to before edge->next==NULL
		for (edge=aet.next; edge!=NULL; edge=edge->next)
		{
			/* Can skip spans that will be zero length */
			if ((edge->x & 0xffff0000) == (edge->link->x & 0xffff0000))
				continue;

			if (edge->x < edge->link->x)
			{
				/* Leading edge: Start new active span */
				span = spare_span;
				spare_span = span->next;

				span->start_x = span->end_x = NOT_VISIBLE;
				span->start_edge = edge;
				span->end_edge = edge->link;
				span->ooz = edge->ooz;
				span->ooz_add = (edge->link->ooz - edge->ooz) / (edge->link->x - edge->x);
				
				/* Find position in active list: this WILL be bounded by BACKGROUND span */
				prev_span = &active;
				next_span = prev_span->next;
/* NOTE: This is a problem, it doesn't work if faces intersect. May need to calc intersection, which requires
2 multiplies and 1 divide. Tried a frig of calcing ooz 5-10 pixels on to try to help with slightly intersecting
spans, but it had a few problems itself. 
				
Other ideas include:
1a. Calcing the exact intersection point using 2 mults and divide
1b.                 "                    using subdivision
2. Only inserting spans for room polygons, and writing a z buffer at the same time, then using it for drawing
the object polygons (ala Quake).
3. Calcing ooz for each span at each pixel and resorting (blarg!)
*/
				while (span->ooz < next_span->ooz + next_span->ooz_add * (edge->x - next_span->start_edge->x))
				{
					prev_span = next_span;
					next_span = prev_span->next;
#ifdef DEBUG_SPAN
if (next_span==NULL)
	S_ExitSystem("Span on active");
#endif
				}

				/* Add in that position */
				span->next = next_span;
				prev_span->next = span;

				/* Is it at top of active list? */
				if (prev_span == &active)
				{
					/* Know it is visible */
					span->start_x = edge->x >> 16;

					/* Place BELOW (on the draw list) the span that is currently top on the active list (next_span); this ensures;
							a) if span below has been cut, it will get drawn first (in correct x order)
							b) if this span is transparent, span below will get draw first
						i.e. it's always the best choice */
					span->draw_prev = next_span;
					span->draw_next = next_span->draw_next;
					if (span->draw_next)
						span->draw_next->draw_prev = span;
					next_span->draw_next = span;
				}
				else
				{
					/* Add to draw list ABOVE prev_span */
					span->draw_next = prev_span;
					span->draw_prev = prev_span->draw_prev;
					span->draw_prev->draw_next = span;
					prev_span->draw_prev = span;

					/* Span may not be top of active list, but may still be visible if one above is a) visible b) transparent */
					if (prev_span->start_x == NOT_VISIBLE || !(prev_span->start_edge->type & TRANSPARENT_TYPE))
						continue;

					span->start_x = edge->x >> 16;
				}

				/* If new span is visible, but opaque, need to cut all visible spans beneath it */
				if (span->start_edge->type & TRANSPARENT_TYPE)
					continue;

				do
				{
					span = span->next;

					/* Mark end of span on draw list */
					if (span->end_x == NOT_VISIBLE)
						span->end_x = edge->x >> 16;
				}
				while (span->start_edge->type & TRANSPARENT_TYPE);
			}
			else
			{
				/* Trailing edge: must have matching span already on list, else there's been an error */
				prev_span = &active;
				while (prev_span->next->end_edge != edge)
					prev_span = prev_span->next;

				/* Got span; now remove it from active list */
				span = prev_span->next;
				prev_span->next = span->next;

				/* If span wasn't visible, then place on spare list (saved drawing it altogether) */
				if (span->start_x == NOT_VISIBLE)
				{
					/* Remove from draw list */
					span->draw_prev->draw_next = span->draw_next;
					span->draw_next->draw_prev = span->draw_prev;

					span->next = spare_span;
					spare_span = span;
				}
				else if (span->end_x == NOT_VISIBLE)
				{
					/* Else needs completing on draw list */
					span->end_x = edge->x >> 16;

					// aet_end shouldn't really be in here; this test is purely for it
					if (span->next == NULL)
						continue;

					/* If it was opaque, then span below is now visible, so needs making visible */
					if (span->start_edge->type & TRANSPARENT_TYPE)
						continue;

					/*	Keep making spans visible until an opaque one is encountered */
					do
					{
						span = span->next;

						/* NOTE: May be visible already because only temporarily obscured */
//						span->start_x = span->start_edge->x >> 16;
						if (span->start_x == NOT_VISIBLE)
							span->start_x = edge->x >> 16; // start span for first time
						else
							span->end_x = NOT_VISIBLE; // restart span (by removing end mark)
					}
					while (span->start_edge->type & TRANSPARENT_TYPE);
				}
			}
		}
		/* DEBUG: At end of STAGE 2, all unused spans should be back on spare list, with the remainder on the draw list */


		/* STAGE 3: Render span list */
		span = draw.next;
		while (span)
		{
#ifdef DEBUG_SPAN
if (span->start_x < 0 || span->end_x > (phd_scrwidth<<16)+0x0ffff)
	S_ExitSystem("Span off screen");
#endif
			if (span->end_x - span->start_x > 0)
				(*RenderSpan[span->start_edge->type])(span, lineptr);

			/* Finished with span, so place back on spare list */
			span->next = spare_span;
			spare_span = span;

			span = span->draw_next;
		}
		lineptr += phd_scrwidth;
		/* DEBUG: At end of STAGE 3, ALL spans should be back on spare list */


		/* STAGE 4: Move and remove active edges*/
		edge = aet.next;
		while (edge->next)
		{
			next = edge->next;

			/* First check that edge hasn't reached its sell by date */
			if (--edge->y)
			{
				edge->ooz += edge->ooz_add;
				edge->x += edge->x_add;
				edge->uoz += edge->uoz_add;
				edge->g += edge->g_add;
				edge->voz += edge->voz_add;
#ifdef DEBUG_SPAN
if (edge->x < 0 || edge->x > (phd_scrwidth<<16)+0x0ffff)
	S_ExitSystem("Edge off screen");
#endif

				/* May have swapped position with things that preceed it in edge table */
				prev = edge->prev;

				if (prev->x > edge->x)
				{
					do
						prev = prev->prev;
					while (prev->x > edge->x);

					/* Remove from old position */
					edge->prev->next = edge->next;
					edge->next->prev = edge->prev;

					/* Add to new */
					edge->prev = prev;
					edge->next = prev->next;
					edge->next->prev = edge;
					prev->next = edge;
				}
			}
			else
			{
				/* Edge has expired, remove from AET and add to spare list again */
				edge->next->prev = edge->prev; // or  next->prev = edge->prev;
				edge->prev->next = edge->next; // and next->prev->next = next;

				edge->next = spare_edge;
				spare_edge = edge;
			}

			edge = next;
		}
	}
	/* DEBUG: At end of screen draw, ALL edges should be back on spare list */
}
#endif




#ifdef WHATS_LEFT
/******************************************************************************
 *		INSERT FLAT-SHADED TRANSPARENT POLYGON - 8 VERTICES
 *****************************************************************************/
int		ins_poly_trans8( PHD_VBUF *vbuf, sint16 shade )
{
	PHD_VBUF	*vn1,*vn2,*vn3,*vn4,*vn5,*vn6,*vn7,*vn8;
	VERTEX_INFO	*v;
	int			npoints;


	vn1 = vbuf;
	vn2 = vbuf+1;
	vn3 = vbuf+2;
	vn4 = vbuf+3;
	vn5 = vbuf+4;
	vn6 = vbuf+5;
	vn7 = vbuf+6;
	vn8 = vbuf+7;

	if ( ( vn1->clip & vn2->clip & vn3->clip & vn4->clip & vn5->clip & vn6->clip & vn7->clip & vn8->clip  ) ||
	       vn1->clip<0 || vn2->clip<0 || vn3->clip<0 || vn4->clip<0 || vn5->clip<0 || vn6->clip<0 || vn7->clip<0 || vn8->clip<0 )
				return(0);

	IF_NOT_VIS(vn1,vn2,vn3)
		return(0);


	if ( (vn1->clip|vn2->clip|vn3->clip|vn4->clip|vn5->clip|vn6->clip|vn7->clip|vn8->clip)==0 )
	{
		*(sort3dptr) = (sint32)( info3dptr );		//insert pointer to info
		*(sort3dptr+1) = vn1->zv/8 + vn2->zv/8 + vn3->zv/8 + vn4->zv/8 + vn5->zv/8 + vn6->zv/8 + vn7->zv/8 + vn8->zv/8;
		sort3dptr += 2;
		*(info3dptr+0)  = DRAW_POLY_TRANS;
		*(info3dptr+1) = shade;		        		//insert Color
		*(info3dptr+2)  = 8;						//8 coords
		*(info3dptr+3)  = (sint16)vn1->xs;
		*(info3dptr+4)  = (sint16)vn1->ys;
		*(info3dptr+5)  = (sint16)vn2->xs;
		*(info3dptr+6)  = (sint16)vn2->ys;
		*(info3dptr+7)  = (sint16)vn3->xs;
		*(info3dptr+8)  = (sint16)vn3->ys;
		*(info3dptr+9)  = (sint16)vn4->xs;
		*(info3dptr+10) = (sint16)vn4->ys;
		*(info3dptr+11) = (sint16)vn5->xs;
		*(info3dptr+12) = (sint16)vn5->ys;
		*(info3dptr+13) = (sint16)vn6->xs;
		*(info3dptr+14) = (sint16)vn6->ys;
		*(info3dptr+15) = (sint16)vn7->xs;
		*(info3dptr+16) = (sint16)vn7->ys;
		*(info3dptr+17) = (sint16)vn8->xs;
		*(info3dptr+18) = (sint16)vn8->ys;
		info3dptr += 19;
		surfacenum++;
	}
	else
	{
		phd_leftfloat = phd_topfloat = 0.0f;
		phd_rightfloat = (float)phd_winxmax;
		phd_bottomfloat = (float)phd_winymax;

		v = v_buffer;
		v->x = (float)vn1->xs;
		v->y = (float)vn1->ys;
		v++;
		v->x = (float)vn2->xs;
		v->y = (float)vn2->ys;
		v++;
		v->x = (float)vn3->xs;
		v->y = (float)vn3->ys;
		v++;
		v->x = (float)vn4->xs;
		v->y = (float)vn4->ys;
		v++;
		v->x = (float)vn5->xs;
		v->y = (float)vn5->ys;
		v++;
		v->x = (float)vn6->xs;
		v->y = (float)vn6->ys;
		v++;
		v->x = (float)vn7->xs;
		v->y = (float)vn7->ys;
		v++;
		v->x = (float)vn8->xs;
		v->y = (float)vn8->ys;

		npoints = XYClipper( 8,v_buffer );
		if ( npoints )
		{
			*(sort3dptr) = (sint32)( info3dptr );		//insert pointer to info
			*(sort3dptr+1) = vn1->zv/8 + vn2->zv/8 + vn3->zv/8 + vn4->zv/8 + vn5->zv/8 + vn6->zv/8 + vn7->zv/8 + vn8->zv/8;
			sort3dptr += 2;
			*(info3dptr+0)  = DRAW_POLY_TRANS;
			*(info3dptr+1) = shade;		        		// insert Color
			*(info3dptr+2)  = npoints;					// number coords
			info3dptr+=3;
			for ( v=v_buffer;npoints>0;npoints--,info3dptr+=2,v++ )
			{
				*(info3dptr) = (sint16)v->x;
				*(info3dptr+1) = (sint16)v->y;
			}
			surfacenum++;
		}
	}
	return(1);
}
#endif