/*
 * $Id$
 *
 * $Date$
 * $Revision$
 *
 * (C) 1999 by Hyperion
 * All rights reserved
 *
 * This file is part of the MiniGL library project
 * See the file Licence.txt for more details
 *
 */

#include "sysinc.h"
#include <math.h>
#include <stdlib.h>

#ifdef DISABLE_TRANSFORMATION
#warning "Compiling without transformation pipeline. Only flat geometry supported"
#endif

static char rcsid[] UNUSED = "$Id$";

#define DUMP_VERTEX(vert)

#define DUMP_VERTEX2(vert)

/*
 * If this macro returns TRUE, the polygon modes are not FILL for both front-
 * and backfacing polygons, and hence require special treatment.
 */
#define NEED_SPECIAL_FILL (context->polygon.PolygonModeFront != GL_FILL \
						|| context->polygon.PolygonModeBack  != GL_FILL)

GLboolean hc_GetFrontFacing(GLcontext context, MGLVertex *a, MGLVertex *b, 
	MGLVertex *c, GLuint outcode);
void d_DrawPoly(GLcontext context, MGLPolygon *poly);
void dh_DrawPolyFF(GLcontext context, MGLPolygon *poly);
void d_DrawPolyExt(GLcontext context, MGLPolygon *poly);
void d_DrawPoints        (GLcontext);
void d_DrawLines         (GLcontext);
void d_DrawLineStrip     (GLcontext);
void d_DrawTriangles     (GLcontext);
void d_DrawTriangleFan   (GLcontext);
void d_DrawTriangleStrip (GLcontext);
void d_DrawQuads         (GLcontext);
void d_DrawPolygon       (GLcontext);
void d_DrawFlatFan       (GLcontext);
void d_DrawQuadStrip     (GLcontext);

INLINE void v_ApplyMatrixElCheapo(GLcontext context, Matrix *pA, int vnum);
INLINE GLvoid v_Transform(GLcontext context);
INLINE void v_ToScreen(GLcontext context, int i);
void m_CombineMatrices(GLcontext context);

extern void m_BuildInverted(GLcontext context);
extern void hc_ClipAndDrawPolyFF(GLcontext context, MGLPolygon *poly, GLuint or_codes);
extern void hc_ClipAndDrawLine(GLcontext context, MGLPolygon *poly, GLuint or_codes);
extern void light_LightVertex(GLcontext context, GLuint vertex, GLfloat area);
extern void hc_ClipLine(GLcontext context, MGLPolygon *poly, GLuint or_codes,
	uint32 *out1, uint32 *out2);
	
#define min(x,y) (x) < (y) ? (x) : (y)
#define max(x,y) (x) > (y) ? (x) : (y)

//extern void hc_ClipLine(GLcontext context, MGLPolygon *poly, GLuint or_codes,
//	uint32 *out1, uint32 *out2);
extern GLboolean hc_LineClip(GLcontext context, uint32 pts[2]);
	
#if MGL_USE_EXACT_SQRT
inline float fast_sqrt(float x)
{
	return (float)sqrt((double)x);
}
#else
inline float fast_sqrt(float x)   //InvSqrt(x) code
{
    float xhalf = 0.5 * x;
    float xprime;
    long i = *((long *)&x);             //transfer to integer register, causes memory access
    i = 0x5F3759DF - (i >> 1);          //poor man's exponent
    xprime = *((float*)&i);             //transfer back to floating point register
    xprime = xprime * (1.5 - xhalf * xprime * xprime);  
    //xprime = xprime * (1.5 - xhalf * xprime * xprime);    //repeat for better accuracy
    return xprime * x;
}
#endif

void SetFlatColor(GLcontext context, GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
	W3D_Color col;
   	col.r = r;
	col.g = g;
	col.b = b;
   	col.a = a;
    IWarp3D->W3D_SetCurrentColor(context->w3dContext, &col);
}

INLINE GLint v_GetFillMode(GLcontext context, GLboolean isFrontFace)
{
	if (isFrontFace) return context->polygon.PolygonModeFront;
	else 			 return context->polygon.PolygonModeBack;
}

// doesn't work if the first three points are colinear
GLint v_GetPolyFillMode(GLcontext context, MGLPolygon *poly)
{
	if (poly->numverts < 3) return GL_FILL;
	
	GLboolean isFrontFace = hc_GetFrontFacing(context,
			&(context->VertexBuffer[poly->verts[0]]),
			&(context->VertexBuffer[poly->verts[1]]),
			&(context->VertexBuffer[poly->verts[2]]),0);
	return v_GetFillMode(context, isFrontFace);
}

INLINE GLboolean v_CheckTriArea(GLfloat threshold, W3D_Vertex *v1, W3D_Vertex *v2, W3D_Vertex *v3)
{
    GLfloat x1 = v2->x - v1->x;
    GLfloat y1 = v2->y - v1->y;
    GLfloat x2 = v3->x - v1->x;
    GLfloat y2 = v3->y - v1->y;
    GLfloat area = y2*x1 - x2*y1;

    if (fabs(area) > threshold)
		return GL_TRUE;
    else
		return GL_FALSE;
}

INLINE GLfloat v_GetTriArea(GLcontext context, int _v1, int _v2, int _v3)
{
	#define v1  context->VertexBuffer[_v1]
	#define v2  context->VertexBuffer[_v2]
	#define v3  context->VertexBuffer[_v3]

    GLfloat x1 = v2.x - v1.x;
    GLfloat y1 = v2.y - v1.y;
    GLfloat x2 = v3.x - v1.x;
    GLfloat y2 = v3.y - v1.y;
    GLfloat area = y2*x1 - x2*y1;


	return area;
			 
	#undef v1
	#undef v2
	#undef v3
	#undef v4
}

INLINE GLboolean v_CheckQuadArea(GLfloat threshold, W3D_Vertex *v1, W3D_Vertex *v2, W3D_Vertex *v3, W3D_Vertex *v4)
{
    GLfloat area = (v1->x*v2->y - v2->y*v1->x)
				 - (v2->x*v3->y - v3->x*v2->y)
				 - (v3->x*v4->y - v4->x*v3->y)
				 - (v4->x*v1->y - v1->x*v4->y);

    if (fabs(area) > threshold)
		return GL_TRUE;
    else
		return GL_FALSE;
}

INLINE GLfloat v_GetQuadArea(GLcontext context, int _v1, int _v2, int _v3, int _v4)
{
	#define v1  context->VertexBuffer[_v1]
	#define v2  context->VertexBuffer[_v2]
	#define v3  context->VertexBuffer[_v3]
	#define v4  context->VertexBuffer[_v4]

    return (v1.x*v2.y - v2.y*v1.x)
		 - (v2.x*v3.y - v3.x*v2.y)
		 - (v3.x*v4.y - v4.x*v3.y)
		 - (v4.x*v1.y - v1.x*v4.y);
		 
	#undef v1
	#undef v2
	#undef v3
	#undef v4
}


inline GLfloat dh_AreaSign(GLcontext context, MGLPolygon *poly)
{
	int i,j;
	GLfloat area=0.0;
	#define x(i) (context->VertexBuffer[poly->verts[i]].x)
	#define y(i) (context->VertexBuffer[poly->verts[i]].y)

	for (i=0; i<poly->numverts; i++)
	{
		j=(i+1)%(poly->numverts);
		area += (x(i)*y(j) - x(j)*y(i));
	}

	#undef x
	#undef y

	// Since our Y coordinates are inverted (i.e. Y = 0 is the top not bottom)
	// we need to use the negative area here
	return -area;
}

void v_GenEyeCoords(GLcontext context)
{
	int vertex;	
	
	if (context->InvRotValid == GL_FALSE) 
		m_BuildInverted(context);
		
#define a(x)  (CurrentMV->v[OF_##x])
#define b(x)  (CurrentP->v[OF_##x])
#define eyex  (context->VertexBuffer[vertex].eye.x)
#define eyey  (context->VertexBuffer[vertex].eye.y)
#define eyez  (context->VertexBuffer[vertex].eye.z)
#define eyew  (context->VertexBuffer[vertex].eye.w)
#define verx  (context->VertexBuffer[vertex].object.x)
#define very  (context->VertexBuffer[vertex].object.y)
#define verz  (context->VertexBuffer[vertex].object.z)
#define verw  (context->VertexBuffer[vertex].object.w)

	for (vertex = 0; vertex < context->VertexBufferPointer; vertex++)
	{	
		eyex = a(11)*verx + a(12)*very + a(13)*verz + a(14)*verw;
		eyey = a(21)*verx + a(22)*very + a(23)*verz + a(24)*verw;
		eyez = a(31)*verx + a(32)*very + a(33)*verz + a(34)*verw;
		eyew = a(41)*verx + a(42)*very + a(43)*verz + a(44)*verw;

		if (eyew != 1.0)
		{
			float one_over_eyew = 1.0/eyew;
			eyex *= one_over_eyew;
			eyey *= one_over_eyew;
			eyez *= one_over_eyew;
			eyew = 1.0;
		}		
	}

#undef a
#undef b
#undef eyex
#undef eyey
#undef eyez
#undef eyew
#undef verx
#undef very
#undef verz
#undef verw
	

#define ver(c)  (context->VertexBuffer[vertex].Normal.c)
#define a(x)    (context->InvRot[x])
#define nx		(context->VertexBuffer[vertex].EyeNormal.x)
#define ny		(context->VertexBuffer[vertex].EyeNormal.y)
#define nz		(context->VertexBuffer[vertex].EyeNormal.z)
		
	if (context->NeedEyeNormal)
	{
		for (vertex = 0; vertex < context->VertexBufferPointer; vertex++)
		{			
			// InvRot is row-major
			nx = ver(x)*a(0) + ver(y)*a(3) + ver(z)*a(6);
			ny = ver(x)*a(1) + ver(y)*a(4) + ver(z)*a(7);
			nz = ver(x)*a(2) + ver(y)*a(5) + ver(z)*a(8);
		}
	}
	
#undef ver
#undef a
#undef nx
#undef ny
#undef nz
}



/*
** This computationally intensive piece of code generates coordinates
** for the reflective image used in spherical environment mapping.
** This is a good place for optimizations.
** After generation, transforms into the clipping space...
**
** Note that this is after the book. It is probably a big time-eater.
**
** (Turned out not to be so time intensive after all...)
*/
void v_GenTexCoords(GLcontext context, int vertex)
{
	float u[4];
	float ul;
	float nx,ny,nz;
	float nu;
	float rx = 0.0, ry = 0.0, rz = 0.0;
	float m = 0.0;
	float rw = 0.0; //surgeon
	int i;
	BOOL needR = FALSE;
	BOOL needM = FALSE;

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		/* If either (or both) gen's are enabled and either SPHERE_MAP ot
		 * REFLECTION_MAP, compute r and mfirst
		 */
		if (context->enable.TexGenS[i])
		{
			if (context->texture.TexGenS[i].needR)
		  		needR = TRUE;
	  	
		  	if (context->texture.TexGenS[i].needM)
		  		needM = TRUE;
		}

		if (context->enable.TexGenT[i])
		{
			if (context->texture.TexGenT[i].needR)
		  		needR = TRUE;
	  	
		  	if (context->texture.TexGenT[i].needM)
		  		needM = TRUE;
		}

	}
	
	if (needR)
	{
		u[0] = context->VertexBuffer[vertex].eye.x;
		u[1] = context->VertexBuffer[vertex].eye.y;
		u[2] = context->VertexBuffer[vertex].eye.z;
		nx = context->VertexBuffer[vertex].EyeNormal.x;
		ny = context->VertexBuffer[vertex].EyeNormal.y;
		nz = context->VertexBuffer[vertex].EyeNormal.z;

		// nomalize unit vector
		ul = (float)fast_sqrt((double)(u[0]*u[0] + u[1]*u[1] + u[2]*u[2]));
		if (ul == 0.f)
			return;
	
		u[0] /= ul; 
		u[1] /= ul; 
		u[2] /= ul;

		nu = nx*u[0] + ny*u[1]+nz*u[2]; 
		nu *= 2.f;

		rx = u[0] - nx*nu;
		ry = u[1] - ny*nu;
		rz = u[2] - nz*nu;

		rw = rz+1.f; //surgeon
	}

	if (needM)
		m = 0.5f / (float)fast_sqrt((double)(rx*rx + ry*ry + rw*rw));
			
	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.TexGenS[i] == GL_TRUE)
		{
			switch (context->texture.TexGenS[i].mode)
			{
				case GL_SPHERE_MAP:
					context->VertexBuffer[vertex].uvw[i].u = rx * m + 0.5;
					break;
				case GL_REFLECTION_MAP:
					context->VertexBuffer[vertex].uvw[i].u = rx;
					break;
				case GL_OBJECT_LINEAR:
					context->VertexBuffer[vertex].uvw[i].u = 
						context->VertexBuffer[vertex].object.x * context->texture.TexGenS[i].objectPlane[0]
					  + context->VertexBuffer[vertex].object.y * context->texture.TexGenS[i].objectPlane[1]
					  + context->VertexBuffer[vertex].object.z * context->texture.TexGenS[i].objectPlane[2]
					  + context->VertexBuffer[vertex].object.w * context->texture.TexGenS[i].objectPlane[3];
					break;
				case GL_EYE_LINEAR:
					context->VertexBuffer[vertex].uvw[i].u = 
						context->VertexBuffer[vertex].object.x * context->texture.TexGenS[i].eyePlane[0]
					  + context->VertexBuffer[vertex].object.y * context->texture.TexGenS[i].eyePlane[1]
					  + context->VertexBuffer[vertex].object.z * context->texture.TexGenS[i].eyePlane[2]
					  + context->VertexBuffer[vertex].object.w * context->texture.TexGenS[i].eyePlane[3];
					break;
				case GL_NORMAL_MAP:
					context->VertexBuffer[vertex].uvw[i].u = 
						context->VertexBuffer[vertex].EyeNormal.x;
					break;
			}
		}

		if (context->enable.TexGenT[i] == GL_TRUE)
		{
			switch (context->texture.TexGenT[i].mode)
			{
				case GL_SPHERE_MAP:
					context->VertexBuffer[vertex].uvw[i].v = ry * m + 0.5;
					break;
				case GL_REFLECTION_MAP:
					context->VertexBuffer[vertex].uvw[i].v = ry;
					break;
				case GL_OBJECT_LINEAR:
					context->VertexBuffer[vertex].uvw[i].v = 
						context->VertexBuffer[vertex].object.x * context->texture.TexGenT[i].objectPlane[0]
					  + context->VertexBuffer[vertex].object.y * context->texture.TexGenT[i].objectPlane[1]
					  + context->VertexBuffer[vertex].object.z * context->texture.TexGenT[i].objectPlane[2]
					  + context->VertexBuffer[vertex].object.w * context->texture.TexGenT[i].objectPlane[3];
					break;
				case GL_EYE_LINEAR:
					context->VertexBuffer[vertex].uvw[i].v = 
						context->VertexBuffer[vertex].object.x * context->texture.TexGenT[i].eyePlane[0]
					  + context->VertexBuffer[vertex].object.y * context->texture.TexGenT[i].eyePlane[1]
					  + context->VertexBuffer[vertex].object.z * context->texture.TexGenT[i].eyePlane[2]
					  + context->VertexBuffer[vertex].object.w * context->texture.TexGenT[i].eyePlane[3];
					break;
				case GL_NORMAL_MAP:
					context->VertexBuffer[vertex].uvw[i].v = 
						context->VertexBuffer[vertex].EyeNormal.y;
					break;
			}
		}
	}
}


INLINE void v_Transform(GLcontext context)
{
	int i, tmu;

	if (context->NeedEye)
		v_GenEyeCoords(context);	

	if (context->enable.TexGen == GL_TRUE)
	{
		for (i=0; i<context->VertexBufferPointer; i++)
			v_GenTexCoords(context, i);
	}

	if (context->CombinedValid == GL_FALSE)
		m_CombineMatrices(context);

	for (tmu = 0; tmu <= context->texture.MaxTextureUnit; tmu++)
	{
		if (!(context->Texture[context->TextureNr[tmu]][tmu].flags & MGLMAT_IDENTITY))
		{
			/* The current texture matrix is not an identity */
		    #define a(x) context->Texture[context->TextureNr[tmu]][tmu].v[OF_##x]
		    float a11 = a(11);
		    float a12 = a(12);
		    float a14 = a(14);
		    float a21 = a(21);
		    float a22 = a(22);
		    float a24 = a(24);
		    float a41 = a(41);
		    float a42 = a(42);
		    float a44 = a(44);

			for (i = 0; i < context->VertexBufferPointer; i++)
			{
				MGLVertex *vx = &context->VertexBuffer[i];
				float u = vx->uvw[tmu].u;
				float v = vx->uvw[tmu].v;
				float w = vx->uvw[tmu].w;
				
				if (vx->rhw_valid == GL_FALSE)
				{
					vx->uvw[tmu].u = a11*u + a12*v + a14;
					vx->uvw[tmu].v = a21*u + a22*v + a24;
				} 
				else
				{
					vx->uvw[tmu].u = a11*u + a12*v + a14*w;
					vx->uvw[tmu].v = a21*u + a22*v + a24*w;
					vx->uvw[tmu].w = a41*u + a42*v + a44*w;
				}
			}
				
			#undef a
		}
	}

	if (context->WOne_Hint == GL_FALSE)
	{
	    #define a(x) (context->CombinedMatrix.v[OF_##x])
	    float a11 = a(11);
	    float a12 = a(12);
	    float a13 = a(13);
	    float a14 = a(14);
	    float a21 = a(21);
	    float a22 = a(22);
	    float a23 = a(23);
	    float a24 = a(24);
	    float a31 = a(31);
	    float a32 = a(32);
	    float a33 = a(33);
	    float a34 = a(34);
	    float a41 = a(41);
	    float a42 = a(42);
	    float a43 = a(43);
	    float a44 = a(44);

	    int counter = context->VertexBufferPointer;
	    MGLVertex *vf = context->VertexBuffer;

	    for (i = 0; i < counter; i++)
	    {
			MGLVertex *v = &vf[i];
			float x = v->object.x;
			float y = v->object.y;
			float z = v->object.z;
			float w = v->object.w;

			v->clip.x = a11*x + a12*y + a13*z + a14*w;
			v->clip.y = a21*x + a22*y + a23*z + a24*w;
			v->clip.z = a31*x + a32*y + a33*z + a34*w;
			v->clip.w = a41*x + a42*y + a43*z + a44*w;
	    }

	    #undef a
	}
	else
	{
	    #define a(x) (context->CombinedMatrix.v[OF_##x])
	    float a11 = a(11);
	    float a12 = a(12);
	    float a13 = a(13);
	    float a14 = a(14);
	    float a21 = a(21);
	    float a22 = a(22);
	    float a23 = a(23);
	    float a24 = a(24);
	    float a31 = a(31);
	    float a32 = a(32);
	    float a33 = a(33);
	    float a34 = a(34);
	    float a41 = a(41);
	    float a42 = a(42);
	    float a43 = a(43);
	    float a44 = a(44);

	    int counter = context->VertexBufferPointer;
	    MGLVertex *vf = context->VertexBuffer;

	    for (i=0; i<counter; i++)
	    {
		    MGLVertex *v = &vf[i];
		    float x = v->object.x;
		    float y = v->object.y;
		    float z = v->object.z;
		    v->clip.x = a11*x + a12*y + a13*z + a14;
		    v->clip.y = a21*x + a22*y + a23*z + a24;
		    v->clip.z = a31*x + a32*y + a33*z + a34;
		    v->clip.w = a41*x + a42*y + a43*z + a44;
	    }
	#undef a
	}
}

#define CLAMP(x,a,b) ( (x) < (a) ? (a) \
			: (x) > (b) ? (b) \
			: (x) )

#define ABS(x) ((x) < 0.0f ? -(x) : (x))

INLINE void v_ToScreen(GLcontext context, int i)
{
	float w;
	int tmu;
	float fogDepth = 0.0f;
	float c = 0.0f;

	w = 1.0 / context->VertexBuffer[i].clip.w;

	context->VertexBuffer[i].x = 
		context->ax + context->VertexBuffer[i].clip.x * w * context->sx;
	context->VertexBuffer[i].y = 
		context->ay - context->VertexBuffer[i].clip.y * w * context->sy;
	context->VertexBuffer[i].z = 
		context->az + context->VertexBuffer[i].clip.z * w * context->sz;

	if (context->enable.ZOffset == GL_TRUE)
		context->VertexBuffer[i].z += (W3D_Float)context->depth_buffer.ZOffset;

	/* Set texture w if it wasn't given in glTexCoord */
	for (tmu = 0; tmu <= context->texture.MaxTextureUnit; tmu++)
	{
		if (context->VertexBuffer[i].rhw_valid[tmu] == GL_FALSE)
			context->VertexBuffer[i].uvw[tmu].w = w;
	}
	
	if (context->UseZFog == GL_FALSE)
	{
		if (context->fog.CurrentFogSource == GL_FRAGMENT_DEPTH)
			context->VertexBuffer[i].fogdepth = w;
		else
			context->VertexBuffer[i].fogdepth = 1/(context->VertexBuffer[i].bf * w * context->sz);
	}
	else
	{
		if (context->InvRotValid == GL_FALSE) 
			m_BuildInverted(context);
			
		GLfloat depth = 0.0f;
		if (context->fog.CurrentFogSource == GL_FRAGMENT_DEPTH)
		{
			if (!context->NeedEye)
			{
				#define a(x)  (CurrentMV->v[OF_##x])
				#define verx  (context->VertexBuffer[i].object.x)
				#define very  (context->VertexBuffer[i].object.y)
				#define verz  (context->VertexBuffer[i].object.z)
				#define verw  (context->VertexBuffer[i].object.w)
				depth = a(31)*verx + a(32)*very + a(33)*verz + a(34)*verw;
				GLfloat eyew = a(41)*verx + a(42)*very + a(43)*verz + a(44)*verw;
	
				if (eyew != 1.0) depth /= eyew;
				#undef verx
				#undef very
				#undef verz
				#undef verw
				#undef a
			}
			else
			{
				depth = context->VertexBuffer[i].eye.z;
			}
		}
		else
		{
			// handle FOG_COORD
		}
		// FIXME: Handle that via a lookup table or similar
		switch(context->fog.FogMode)
		{
		case GL_LINEAR:
			context->VertexBuffer[i].fogdepth = (context->fog.FogEnd - depth)
										/ (context->fog.FogEnd - context->fog.FogStart);
			fogDepth = (context->fog.FogEnd - ABS(depth))
					 / (context->fog.FogEnd - context->fog.FogStart);
			break;
		case GL_EXP:
			c = context->fog.FogDensity * ABS(depth);
			fogDepth = exp(-c);
			break;
		case GL_EXP2:
			c = context->fog.FogDensity * ABS(depth);
			fogDepth = exp(-(c*c));
			break;
		}
		context->VertexBuffer[i].fogdepth = CLAMP(fogDepth, 0.0f, 1.0f);
	}
}


void d_ClipAndDrawPolyPoint(GLcontext context, MGLPolygon *poly, uint32 or_code)
{
	uint32 *idx = (uint32 *)context->IndexBuffer;
	uint32 num = 0;
	
	for (int i=0; i<poly->numverts; i++)
	{
		if (context->VertexBuffer[poly->verts[i]].outcode == 0)
		{
			*idx++ = poly->verts[i];
			num++;
			v_ToScreen(context, poly->verts[i]);
		}
	}
	if (num != 0) // Anything left after clipping?
	{
		// Draw indexed, some points are off
		IWarp3D->W3D_DrawElements(context->w3dContext, 
			W3D_PRIMITIVE_POINTS, W3D_INDEX_ULONG,
			num, context->IndexBuffer);
	}
}

void d_ClipAndDrawPolyLine(GLcontext context, MGLPolygon *poly, uint32 or_code)
{
	for (int i = 0; i< poly->numverts; i++)
	{
		v_ToScreen(context, poly->verts[i]);
	}
	
	int cur = 0;
	int prev = poly->numverts-1;
	int i = poly->numverts;
	
	while (i)
	{
		#if 1
		uint32 pts[2];
		// Draw a line between prev and next
		pts[0] = poly->verts[prev];
		pts[1] = poly->verts[cur];
		if (hc_LineClip(context, pts))
		{
			v_ToScreen(context, pts[0]);
			v_ToScreen(context, pts[1]);
			IWarp3D->W3D_DrawElements(context->w3dContext,
				W3D_PRIMITIVE_LINES, W3D_INDEX_ULONG,
				2, pts);
		}
		#else
		MGLPolygon p;
		GLuint or_code = context->VertexBuffer[poly->verts[prev]].outcode
					   | context->VertexBuffer[poly->verts[cur]].outcode;
		p.numverts = 2;
		p.verts[0] = poly->verts[prev];
		p.verts[1] = poly->verts[cur];
		hc_ClipAndDrawLine(context, &p, or_code);
		#endif
		--i;
		prev = cur;
		cur++;
	}
}

void d_ClipAndDrawPolyWithFill(GLcontext context, MGLPolygon *poly, 
	uint32 or_code, GLuint fillMode)
{
	switch(fillMode)
	{
	case GL_POINT:
		d_ClipAndDrawPolyPoint(context, poly, or_code);
		break;
	case GL_LINE:
		d_ClipAndDrawPolyLine(context, poly, or_code);
		break;
	case GL_FILL:
		hc_ClipAndDrawPoly(context, poly, or_code);
		break;
	}
}


#ifndef max
#define max(x,y) (x) > (y) ? (x) : (y)
#endif

/* FIXME: Reuse polygon area */
GLfloat v_PolygonOffset(GLcontext context, int v1, int v2, int v3)
{
	#define vert(i) context->VertexBuffer[i]
	GLfloat dx1 = vert(v1).x - vert(v3).x;
	GLfloat dx2 = vert(v2).x - vert(v3).x;
	GLfloat dy1 = vert(v1).y - vert(v3).y;
	GLfloat dy2 = vert(v2).y - vert(v3).y;
	GLfloat dz1 = vert(v1).z - vert(v3).z;
	GLfloat dz2 = vert(v2).z - vert(v3).z;
	GLfloat iarea = 1.0 / (dx1*dy2 - dx2*dy1);
	GLfloat dxdz = iarea * (dy1*dz2 - dz1*dy2);
	GLfloat dydz = iarea * (dz1*dx2 - dx1*dz2);
	
	if (dxdz < 0) 
		dxdz = -dxdz;
	
	if (dydz < 0)
		dydz = -dydz;
		
	return context->polygon.PolygonOffsetUnits*context->DepthBufferUnit
	     + context->polygon.PolygonOffsetFactor * (max(dxdz, dydz));
}

void d_DrawPoints(GLcontext context)
{
	int i;
	uint32 *idx = (uint32 *)context->IndexBuffer;
	uint32 num = 0;
	
	v_Transform(context);
	for (i=0; i<context->VertexBufferPointer; i++)
	{
		/* First of all, code the point and see if it is inside */
		hc_CodePoint(context, &(context->VertexBuffer[i]));
		if (context->VertexBuffer[i].outcode == 0)
		{
			/* Any point inside the clip volume is added to the index array */
			*idx++ = i;
			num++;
			v_ToScreen(context, i);
		}
	}
	
	if (num != 0) // Anything left after clipping?
	{
		if (num < context->VertexBufferPointer - 1)
		{
			// Draw indexed, some points are off
			IWarp3D->W3D_DrawElements(context->w3dContext, 
				W3D_PRIMITIVE_POINTS, W3D_INDEX_ULONG,
				num, context->IndexBuffer);
		}
		else
		{
			// Draw in one go, all of them are onscreen
			IWarp3D->W3D_DrawArray(context->w3dContext,
				W3D_PRIMITIVE_POINTS, 0, num);
		}
	}
}


void d_DrawLines(GLcontext context)
{
/* FIXME: Implement them in the driver, or do a replacement here */
    int i;
    // W3D_Line lin;
    GLuint or_code, and_code;
    ULONG error;
    v_Transform(context);


    for (i=0; i<context->VertexBufferPointer; i+=2)
    {
		hc_CodePoint(context, &(context->VertexBuffer[i]));
		hc_CodePoint(context, &(context->VertexBuffer[i+1]));

		or_code = context->VertexBuffer[i+0].outcode
				| context->VertexBuffer[i+1].outcode;

		and_code = context->VertexBuffer[i+0].outcode
				 & context->VertexBuffer[i+1].outcode;
	
		if (and_code) 
			continue;

		if (or_code == 0)
		{
		    v_ToScreen(context, i);
		    v_ToScreen(context, i+1);
		    
		    if (context->enable.Lighting)
		    {
		    	light_LightVertex(context, i, 1);
		    	light_LightVertex(context, i+1, 1);
		    }
		    #if 0
		    lin.v1.x = context->VertexBuffer[i+0].x;
		    lin.v1.y = context->VertexBuffer[i+0].y;
		    lin.v1.z = context->VertexBuffer[i+0].z;
		    lin.v1.w = context->VertexBuffer[i+0].uvw[0].w;
		    lin.v1.color.r = context->VertexBuffer[i+0].r;
		    lin.v1.color.g = context->VertexBuffer[i+0].g;
		    lin.v1.color.b = context->VertexBuffer[i+0].b;
		    lin.v1.color.a = context->VertexBuffer[i+0].a;
		    lin.v2.x = context->VertexBuffer[i+1].x;
		    lin.v2.y = context->VertexBuffer[i+1].y;
		    lin.v2.z = context->VertexBuffer[i+1].z;
		    lin.v2.w = context->VertexBuffer[i+1].uvw[0].w;
		    lin.v2.color.r = context->VertexBuffer[i+1].r;
		    lin.v2.color.g = context->VertexBuffer[i+1].g;
		    lin.v2.color.b = context->VertexBuffer[i+1].b;
		    lin.v2.color.a = context->VertexBuffer[i+1].a;
		    lin.tex = 0; //context->w3dTexBuffer[context->CurrentBinding];
		    lin.st_pattern = 0;
		    lin.st_enable = 0;
		    lin.st_factor = 1;
		    lin.linewidth = context->LineWidth;
		    error = IWarp3D->W3D_DrawLine(context->w3dContext, &lin);
		    #else
		    error = IWarp3D->W3D_DrawArray(context->w3dContext, 
		    	W3D_PRIMITIVE_LINES, i, 2);
		    #endif
		}
		else
		{
		    static MGLPolygon poly;
		    poly.numverts = 2;
		    poly.verts[0] = i;
		    poly.verts[1] = i+1;
		    hc_ClipAndDrawLine(context, &poly, or_code);
		}
    }
}

void dh_DrawLine(GLcontext context, MGLPolygon *poly)
{
/* FIXME: Implement in driver, or replace */
    ULONG error;
    // static W3D_Line lin;

    v_ToScreen(context, poly->verts[0]);
    v_ToScreen(context, poly->verts[1]);

    if (context->enable.Lighting)
    {
    	light_LightVertex(context, 0, 1);
    	light_LightVertex(context, 1, 1);
    }
    #if 0
    lin.v1.x = context->VertexBuffer[poly->verts[0]].x;
	lin.v1.y = context->VertexBuffer[poly->verts[0]].y;
	lin.v1.z = context->VertexBuffer[poly->verts[0]].z;
	lin.v1.w = context->VertexBuffer[poly->verts[0]].uvw[0].w;
    lin.v1.color.r = context->VertexBuffer[0].r;
    lin.v1.color.g = context->VertexBuffer[0].g;
    lin.v1.color.b = context->VertexBuffer[0].b;
    lin.v1.color.a = context->VertexBuffer[0].a;
	lin.v2.x = context->VertexBuffer[poly->verts[1]].x;
	lin.v2.y = context->VertexBuffer[poly->verts[1]].y;
	lin.v2.z = context->VertexBuffer[poly->verts[1]].z;
	lin.v2.w = context->VertexBuffer[poly->verts[1]].uvw[0].w;
    lin.v2.color.r = context->VertexBuffer[1].r;
    lin.v2.color.g = context->VertexBuffer[1].g;
    lin.v2.color.b = context->VertexBuffer[1].b;
    lin.v2.color.a = context->VertexBuffer[1].a;

    lin.tex = 0;
    lin.st_pattern = 0;
    lin.st_enable = 0;
    lin.st_factor  = 1;
    lin.linewidth = context->LineWidth;

    error = IWarp3D->W3D_DrawLine(context->w3dContext, &lin);
    #else
    error = IWarp3D->W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_LINES,
    	W3D_INDEX_ULONG, 2, poly->verts);
    #endif
}

#define NEXT_IDX(a) (((a)+1)%context->VertexBufferPointer)
#define PREV_IDX(a) ((a) == 0?context->VertexBufferPointer-1:(a)-1)

#define IS_INSIDE(a) ((a) == 0)
#define IS_OUTSIDE(a) ((a) != 0)

#ifndef MAX
#define MAX(a,b) ( (a) > (b) ? (a) : (b) )
#endif

#ifndef MIN
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
#endif

void d_DrawLineStrip(GLcontext context)
{
	GLuint or_code, and_code;
    ULONG error;
    v_Transform(context);

	or_code = 0;
	and_code = 0;
	
    for (int i=0; i<context->VertexBufferPointer; i++)
    {
		hc_CodePoint(context, &(context->VertexBuffer[i]));

		or_code  |= context->VertexBuffer[i+0].outcode;
		and_code &= context->VertexBuffer[i+0].outcode;
    }
    
    if (and_code) return;
     
    if (or_code == 0)
    {
    	// nice case - everything is on-screen
    	uint32 prim = W3D_PRIMITIVE_LINESTRIP;
    	
    	if (context->CurrentPrimitive == GL_LINE_LOOP)
    		prim = W3D_PRIMITIVE_LINELOOP;
    	
		for (int i = 0; i < context->VertexBufferPointer; i++)
			v_ToScreen(context, i);
    	
    	error = IWarp3D->W3D_DrawArray(context->w3dContext,
    		prim, 0, context->VertexBufferPointer);
    }
    else
    {
		/*
		 * If there is at least one vertex outside the screen, we need to draw
		 * the whole loop/strip as a series of line strips. We start at the 
		 * first vertex (or the last vertex if it is a loop) and move along
		 * the lines of the strip, adding points to the index array as we
		 * go. 
		 */
		int count = 0; // Number of points in the current loop.
		int currentIndex;
		int primSize = context->VertexBufferPointer;
		if (context->CurrentPrimitive == GL_LINE_LOOP)
		{
			currentIndex = primSize-1;
		}
		else
		{
			currentIndex = 0;
		}
		/*
		 * If the starting point is on-screen, we output it prior to the 
		 * inner loop, because we will always add endpoints of the line.
		 */
		if (context->VertexBuffer[currentIndex].outcode == 0)
		{
			context->IndexBuffer[count] = currentIndex;
			v_ToScreen(context, currentIndex);
			count ++;
		}
		
		currentIndex = NEXT_IDX(currentIndex);
		
		//while (currentIndex < primSize)
		for (int vtxcount = 0; vtxcount < primSize; vtxcount++)
		{
			/*
			 * The inner loop starts here
			 * There are four cases we have to take care of
			 * 1) we are outside and stay outside
			 * 2) we are outside but pass into the inside
			 * 3) we are inside and stay inside
			 * 4) we are inside and pass into the outside
			 * 
			 * if 1) do nothing 
			 * if 2) clip the line segment, adding the new point as the
			 * 		 start of a new loop
			 * if 3) add the current point to the line loop
			 * if 4) clip the line segment, the add new (clipped) point to the 
			 * output, then draw it.
			 * 
			 * There is actually one more case which is borderline - the place
			 * when we hit the end of the actual loop/strip. This is not
			 * strictly part of the inner loop, and hence will be handled after
			 * the end of the while loop if there is an incomplete strip 
			 * waiting.
			 * 
			 * Note that the points we usually add are the endpoints of the
			 * line segments.
			 */
			int thisOutcode = context->VertexBuffer[currentIndex].outcode;
			int prevOutcode = 
				context->VertexBuffer[PREV_IDX(currentIndex)].outcode;
			// we don't handle case 1. There is nothing to do but increment
			// the counter
			
			if (IS_OUTSIDE(prevOutcode) && IS_INSIDE(thisOutcode))
			{
				// case 2
				#ifdef OBSOLETE_CODE
				uint32 out1 = 0, out2 = 0;
				MGLPolygon line;
				line.verts[0] = currentIndex;
				line.verts[1] = PREV_IDX(currentIndex);
				line.numverts = 2;
				hc_ClipLine(context, &line, thisOutcode | prevOutcode, 
					&out1, &out2);
				#endif
				uint32 pts[2];
				pts[0] = currentIndex;
				pts[1] = PREV_IDX(currentIndex);
				// can ignore result - this is never rejected completely
				hc_LineClip(context, pts);
				uint32 out1 = pts[0];
				uint32 out2 = pts[1];
				count = 0; // start a new strip here

				// add the clipped point...
				if (out1 >= primSize) // out1 is new
				{
					context->IndexBuffer[count] = out1;
					context->VertexBufferPointer = 
						MAX(context->VertexBufferPointer, out1+1);
					v_ToScreen(context, out1);
				}
				else
				{
					context->IndexBuffer[count] = out2;
					context->VertexBufferPointer =
						MAX(context->VertexBufferPointer, out2+1);
					v_ToScreen(context, out2);
				}
								
				count++;
				
				// ... and the endpoint
				context->IndexBuffer[count] = currentIndex;
				v_ToScreen(context, currentIndex);
				count++;
			}
			else if (IS_INSIDE(prevOutcode) && IS_INSIDE(thisOutcode))
			{
				// case 3
				context->IndexBuffer[count] = currentIndex;
				v_ToScreen(context, currentIndex);
				count++;
			}
			else if (IS_INSIDE(prevOutcode) && IS_OUTSIDE(thisOutcode))
			{
				// case 4
				#ifdef OBSOLETE_CODE
				uint32 out1 = 0, out2 = 0;
				 MGLPolygon line;
				line.verts[0] = currentIndex;
				line.verts[1] = PREV_IDX(currentIndex);
				line.numverts = 2;
				hc_ClipLine(context, &line, thisOutcode | prevOutcode, 
					&out1, &out2);
				#endif
				
				uint32 pts[2];
				pts[0] = currentIndex;
				pts[1] = PREV_IDX(currentIndex);
				// can ignore result - this is never rejected completely
				hc_LineClip(context, pts);
				uint32 out1 = pts[0];
				uint32 out2 = pts[1];
				// add the clipped point
				if (out1 >= primSize) // out1 is new
				{
					context->IndexBuffer[count] = out1;
					context->VertexBufferPointer = 
						MAX(context->VertexBufferPointer, out1+1);
					v_ToScreen(context, out1);
				}
				else
				{
					context->IndexBuffer[count] = out2;
					context->VertexBufferPointer =
						MAX(context->VertexBufferPointer, out2+1);
					v_ToScreen(context, out2);
				}

				count++;
				// draw the strip
				IWarp3D->W3D_DrawElements(context->w3dContext, 
					W3D_PRIMITIVE_LINESTRIP,
				    	W3D_INDEX_ULONG, count, 
				    	context->IndexBuffer);
				count = 0;
			}
			else 
			{
				uint32 and_code = prevOutcode & thisOutcode;
				if (!and_code)
				{
					uint32 pts[2];
					pts[0] = PREV_IDX(currentIndex);
					pts[1] = currentIndex;
					
					MGLPolygon line;
					int prev = PREV_IDX(currentIndex);
					line.verts[1] = MAX(currentIndex, prev);
					line.verts[0] = MIN(currentIndex, prev);
					line.numverts = 2;
					hc_ClipAndDrawLine(context, &line, 
						thisOutcode | prevOutcode);
				}
			}

			currentIndex = NEXT_IDX(currentIndex);
			//if (currentIndex == primSize/*-1*/) break;
		}
		
		if (count)
		{
			IWarp3D->W3D_DrawElements(context->w3dContext, 
					W3D_PRIMITIVE_LINESTRIP,
				    	W3D_INDEX_ULONG, count, 
				    	context->IndexBuffer);
		}
    }
}
//surgeon: added fast path when primitive or_code equals 0

#define CLIP_EPS (1e-7)

void d_DrawTriangleFan(GLcontext context)
{
	int i;
	GLuint and_code, or_code, local_or, local_and;
	ULONG error;
	static W3D_Vertex **verts = NULL;
	static GLboolean visible[MGL_MAXVERTS];     // Should be enough...?
	static GLubyte   complete[MGL_MAXVERTS];    // Ditto
	int triangle = 0;

	if (verts == NULL)
	{
		verts = (W3D_Vertex **)IExec->AllocVec(sizeof(W3D_Vertex *) * context->VertexBufferSize, MEMF_ANY);
		if (!verts) 
			return;
	}

	if(context->enable.CullFace == GL_TRUE 
	&& context->polygon.CullFace == GL_FRONT_AND_BACK)
	    return;

	v_Transform(context);

	and_code = 0xFF;
	or_code = 0;

	for (i=0; i<context->VertexBufferPointer; i++)
	{
		MGLVertex *v = &(context->VertexBuffer[i]);
		float w = v->clip.w;
		GLuint local_outcode = 0;

		if (w < CLIP_EPS )
			local_outcode |= MGL_CLIP_NEGW;

		if (-w > v->clip.x)
			local_outcode |= MGL_CLIP_LEFT;
		else if (v->clip.x > w)
			local_outcode |= MGL_CLIP_RIGHT;

		if (-w > v->clip.y)
			local_outcode |= MGL_CLIP_BOTTOM;
		else if (v->clip.y > w)
			local_outcode |= MGL_CLIP_TOP;

		if (-w > v->clip.z)
			local_outcode |= MGL_CLIP_BACK;
		else if (v->clip.z > w)
			local_outcode |= MGL_CLIP_FRONT;

		v->outcode = local_outcode;
		and_code &= local_outcode;
		or_code  |= local_outcode;
	}

	if (and_code) 
		return;

	if (context->enable.Lighting)
	{
		GLfloat area = v_GetTriArea(context, 0, 1, 2);
		
		light_LightVertex(context, 0, area);
		light_LightVertex(context, 1, area);
		
		for (i = 2; i < context->VertexBufferPointer; i++)
		{
			area = v_GetTriArea(context, 0, i-1, i);
			light_LightVertex(context, i, area);
		}
	}
		
	if (or_code == 0 && !NEED_SPECIAL_FILL) //primitive completely visible
	{
	   	int i;

		if (context->enable.CullFace)
			IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, W3D_ENABLE);

		for (i = 0; i < context->VertexBufferPointer; i++)
			v_ToScreen(context, i);

		IWarp3D->W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, 0, 
			context->VertexBufferPointer);

		if (context->enable.CullFace)
			IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, W3D_DISABLE);

		return;
	}
	
	if (!NEED_SPECIAL_FILL)
	{
		/*
		** Up to now everything is like a polygon. Unlike a polygon, however,
		** triangle fans and strips may be concave, and each individual triangle
		** might be backfacing.
		**
		** We calculate how many of the triangles are visible.
		*/
		for (i=1,triangle=0; i<context->VertexBufferPointer - 1; i++, triangle++)
		{
			local_or = context->VertexBuffer[0].outcode
					| context->VertexBuffer[i].outcode
					| context->VertexBuffer[i+1].outcode;

			local_and = context->VertexBuffer[0].outcode
					& context->VertexBuffer[i].outcode
					& context->VertexBuffer[i+1].outcode;


			if (local_and == 0) // if the local and code is zero, we're not
			{                   // completely off screen.
				visible[triangle] = hc_DecideFrontface(context, 
					&(context->VertexBuffer[0]), 
					&(context->VertexBuffer[i]), 
					&(context->VertexBuffer[i+1]), local_or);

			// if our local or codes are zero, we are completely
			// visible

				complete[triangle] = local_or;
			}
			else
			{
				visible[triangle] = GL_FALSE;
			}
		}



		/*
		** Draw...
		** There are three cases:
		**
		** 1) Triangle is partially visible
		** 2) Triangle is fully visible
		** 3) Triangle is invisible
		**
		** In case 1, we draw the triangle with clip_ClipAndDrawPoly.
		** In case 2, we collect a triangle fan/strip of maximum length
		** and draw that directly
		** in case 3... WE IGNORE IT! HA!
		*/
		triangle = 0; i=1;
	
		do
		{
			if (visible[triangle] == GL_FALSE) // case 3
			{
				triangle ++;
				i        ++;
			}
			else
			{
				if (complete[triangle]) // case 1
				{
					static MGLPolygon poly;
					poly.numverts = 3;
					poly.verts[0] = 0;
					poly.verts[1] = i;
					poly.verts[2] = i + 1;
					// HERE
					hc_ClipAndDrawPoly(context, &poly, complete[triangle] );
					triangle++;
					i++;
				}
				else
				{   // case 2 (the difficult part)
					int k = 3;
					
					context->IndexBuffer[0] = 0;
					context->IndexBuffer[1] = i;
					context->IndexBuffer[2] = i+1;
					
					v_ToScreen(context, 0);
					v_ToScreen(context, i);
					v_ToScreen(context, i+1);

					triangle ++;
					i        ++;

					while (complete[triangle]==0 && visible[triangle] && i<context->VertexBufferPointer - 1)
					{
						context->IndexBuffer[k] = i+1;
						v_ToScreen(context, i+1);
						i++; 
						k++; 
						triangle++;
					}
				
					error = IWarp3D->W3D_DrawElements(context->w3dContext, 
							W3D_PRIMITIVE_TRIFAN, W3D_INDEX_ULONG, k, context->IndexBuffer);
				}
			}
		} while (i<context->VertexBufferPointer-1);
	}
	else
	{
		// Fill mode dictates we need to draw these singular
		for (i=1; i<context->VertexBufferPointer - 1; i++)
		{
			MGLPolygon poly;

			local_and = context->VertexBuffer[0].outcode
					& context->VertexBuffer[i].outcode
					& context->VertexBuffer[i+1].outcode;
			
			if (local_and) continue;
			poly.numverts = 3;
			poly.verts[0] = 0;
			poly.verts[1] = i;
			poly.verts[2] = i+1;
			local_or = context->VertexBuffer[0].outcode
					 | context->VertexBuffer[i].outcode
					 | context->VertexBuffer[i+1].outcode;
					 
			GLuint fillMode = v_GetPolyFillMode(context, &poly);
			d_ClipAndDrawPolyWithFill(context, &poly, or_code, fillMode);
		}
	}

}
;
//surgeon: added fast path when primitive or_code equals 0

void d_DrawTriangleStrip(GLcontext context)
{
	int i;
	GLuint and_code, or_code, local_or, local_and;
	ULONG error;
	static W3D_Vertex **verts = NULL;
	static GLboolean visible[MGL_MAXVERTS];     // Should be enough...?
	static GLubyte   complete[MGL_MAXVERTS];    // Ditto
	int triangle = 0;
	GLenum CurrentFrontFace = context->polygon.FrontFace;

	if (verts == NULL)
	{
		verts = (W3D_Vertex **)IExec->AllocVec(sizeof(W3D_Vertex *) * context->VertexBufferSize, MEMF_ANY);
		if (!verts) 
		return;
	}

	if(context->enable.CullFace == GL_TRUE 
	&& context->polygon.CullFace == GL_FRONT_AND_BACK)
		return;

	v_Transform(context);

	and_code = 0xFF;
	or_code = 0;

	for (i=0; i<context->VertexBufferPointer; i++)
	{
		MGLVertex *v = &(context->VertexBuffer[i]);
		float w = v->clip.w;
		GLuint local_outcode = 0;

		if (w < CLIP_EPS )
		{
			local_outcode |= MGL_CLIP_NEGW;
		}

		if (-w > v->clip.x)
		{
			local_outcode |= MGL_CLIP_LEFT;
		}
		else
		if (v->clip.x > w)
		{
			local_outcode |= MGL_CLIP_RIGHT;
		}

		if (-w > v->clip.y)
		{
			local_outcode |= MGL_CLIP_BOTTOM;
		}
		else
		if (v->clip.y > w)
		{
			local_outcode |= MGL_CLIP_TOP;
		}

		if (-w > v->clip.z)
		{
			local_outcode |= MGL_CLIP_BACK;
		}
		else
		if (v->clip.z > w)
		{
			local_outcode |= MGL_CLIP_FRONT;
		}
		v->outcode = local_outcode;
		and_code &= local_outcode;
		or_code  |= local_outcode;
	}

	if (and_code) return;

	if (context->enable.Lighting)
	{
		GLfloat area = v_GetTriArea(context, 0, 1, 2);
		
		light_LightVertex(context, 0, area);
		light_LightVertex(context, 1, area);
		light_LightVertex(context, 2, area);
			
		for (i = 3; i < context->VertexBufferPointer; i++)
		{
			area = v_GetTriArea(context, i-2, i-1, i);
			light_LightVertex(context, i, area);
		}
	}

	if (or_code == 0 && !NEED_SPECIAL_FILL) //primitive completely visible
	{
		int i;
	
		if (context->enable.CullFace)
			IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, W3D_ENABLE);
	
		for (i = 0; i < context->VertexBufferPointer; i++)
			v_ToScreen(context, i);

		IWarp3D->W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRISTRIP, 0,
			context->VertexBufferPointer);

		if (context->enable.CullFace)
			IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, W3D_DISABLE);

		return;
	}
	
	if (!NEED_SPECIAL_FILL)
	{
		/*
		** Up to now everything is like a polygon. Unlike a polygon, however,
		** triangle fans and strips may be concave, and each individual triangle
		** might be backfacing.
		**
		** We calculate how many of the triangles are visible.
		*/
		for (i=0,triangle=0; i<context->VertexBufferPointer - 2; i++, triangle++)
		{
			local_or = context->VertexBuffer[i+0].outcode
					 | context->VertexBuffer[i+1].outcode
					 | context->VertexBuffer[i+2].outcode;
	
			local_and = context->VertexBuffer[i+0].outcode
					  & context->VertexBuffer[i+1].outcode
					  & context->VertexBuffer[i+2].outcode;
	
	
			if (local_and == 0) // if the local and code is zero, we're not
			{                   // completely off screen.
				visible[triangle] = hc_DecideFrontface(context, &(context->VertexBuffer[i+0]), &(context->VertexBuffer[i+1]), &(context->VertexBuffer[i+2]), local_or);
	
	
				// if our local or codes are zero, we are completely
				// visible
				complete[triangle] = local_or;
			}
			else
			{
				visible[triangle] = GL_FALSE;
			}
	
			if (context->polygon.FrontFace == GL_CCW) 
				context->polygon.FrontFace = GL_CW;
			else
				context->polygon.FrontFace = GL_CCW;
		}
		
		/*
		** Draw...
		** There are three cases:
		**
		** 1) Triangle is partially visible
		** 2) Triangle is fully visible
		** 3) Triangle is invisible
		**
		** In case 1, we draw the triangle with clip_ClipAndDrawPoly.
		** In case 2, we collect a triangle fan/strip of maximum length
		** and draw that directly
		** in case 3... WE IGNORE IT! HA!
		*/
		triangle = 0; i=0;
	
		context->polygon.FrontFace = CurrentFrontFace;
	
		do
		{
	
			if (visible[triangle] == GL_FALSE) // case 3
			{
				triangle ++;
				i        ++;
			}
			else
			{
				if (complete[triangle]) // case 1
				{
					static MGLPolygon poly;
	
					poly.numverts = 3;
					poly.verts[0] = i + 0;
					poly.verts[1] = i + 1;
					poly.verts[2] = i + 2;
	
					hc_ClipAndDrawPoly(context, &poly, complete[triangle] );
					triangle++;
					i++;
				}
				else
				{   // case 2 (the difficult part)
					int k = 3;
					int base = i;
					
					v_ToScreen(context, i);
					v_ToScreen(context, i+1);
					v_ToScreen(context, i+2);
					triangle ++;
					i        ++;
	
					while (complete[triangle]==0 && visible[triangle] && i<context->VertexBufferPointer - 2)
					{
						v_ToScreen(context, i+2);
						i++; 
						k++;
						triangle++;
					}
	
					error = IWarp3D->W3D_DrawArray(context->w3dContext,
						W3D_PRIMITIVE_TRISTRIP, base, k);
				}
			}
			if (context->polygon.FrontFace == GL_CCW) 
				context->polygon.FrontFace = GL_CW;
			else
				context->polygon.FrontFace = GL_CCW;
		} while (i<context->VertexBufferPointer - 2);
	}
	else
	{
		// Fill mode dictates we need to draw these singular
		for (i=1; i<context->VertexBufferPointer - 2; i++)
		{
			MGLPolygon poly;

			local_and = context->VertexBuffer[i+0].outcode
					& context->VertexBuffer[i+1].outcode
					& context->VertexBuffer[i+2].outcode;
			
			if (local_and) continue;
			poly.numverts = 3;
			poly.verts[0] = i+0;
			poly.verts[1] = i+1;
			poly.verts[2] = i+2;
			
			local_or = context->VertexBuffer[i+0].outcode
					 | context->VertexBuffer[i+1].outcode
					 | context->VertexBuffer[i+2].outcode;
					 
			GLuint fillMode = v_GetPolyFillMode(context, &poly);
			d_ClipAndDrawPolyWithFill(context, &poly, or_code, fillMode);
		}
		if (context->polygon.FrontFace == GL_CCW) 
			context->polygon.FrontFace = GL_CW;
		else
			context->polygon.FrontFace = GL_CCW;
	}
	context->polygon.FrontFace = CurrentFrontFace;
}


void d_DrawQuadStrip(GLcontext context)
{
	int i;
	GLuint or_code, and_code;
	ULONG error;

	v_Transform(context);

	for (i=0; i<context->VertexBufferPointer-2; i+=2)
	{
		hc_CodePoint(context, &(context->VertexBuffer[i]));
		hc_CodePoint(context, &(context->VertexBuffer[i+1]));
		hc_CodePoint(context, &(context->VertexBuffer[i+2]));
		hc_CodePoint(context, &(context->VertexBuffer[i+3]));

		or_code = context->VertexBuffer[i+0].outcode
				| context->VertexBuffer[i+1].outcode
				| context->VertexBuffer[i+2].outcode
				| context->VertexBuffer[i+3].outcode;

		and_code = context->VertexBuffer[i+0].outcode
				 & context->VertexBuffer[i+1].outcode
				 & context->VertexBuffer[i+2].outcode
				 & context->VertexBuffer[i+3].outcode;

		if (and_code) continue;

		if (hc_DecideFrontface(context, &(context->VertexBuffer[i]),
			&(context->VertexBuffer[i+1]),
			&(context->VertexBuffer[i+2]), or_code) == GL_FALSE) continue;

		if (context->enable.Lighting)
		{
			/* Triangle area is enough for lighting */
			GLfloat area = v_GetTriArea(context, 0, 1, 2);
			
			light_LightVertex(context, 0, area);
			light_LightVertex(context, 1, area);
			light_LightVertex(context, 2, area);
			light_LightVertex(context, 3, area);
						
			for (i = 4; i < context->VertexBufferPointer; i+=2)
			{
				area = v_GetTriArea(context, i-2, i-1, i);
				light_LightVertex(context, i, area);
				light_LightVertex(context, i+1, area);
			}
		}
		
		if (or_code == 0 && !NEED_SPECIAL_FILL)
		{
			v_ToScreen(context, i);
			v_ToScreen(context, i+1);
			v_ToScreen(context, i+2);
			v_ToScreen(context, i+3);

//			if (GL_TRUE == v_CheckQuadArea(context->MinTriArea, verts[0], verts[1], verts[2], verts[3]))
			{

			    if (context->lighting.ShadeModel == GL_FLAT)
			    {
			    	W3D_Color col;
			    	col.r = context->VertexBuffer[i].r;
			    	col.g = context->VertexBuffer[i].g;
			    	col.b = context->VertexBuffer[i].b;
			    	col.a = context->VertexBuffer[i].a;
				    IWarp3D->W3D_SetCurrentColor(context->w3dContext, &col);
			    }

				error = IWarp3D->W3D_DrawArray(context->w3dContext, 
					W3D_PRIMITIVE_TRISTRIP, i, 4);
			}
//			else
//			    error = W3D_SUCCESS;
		}
		else
		{
			static MGLPolygon poly;
			poly.numverts = 4;
			poly.verts[0] = i;
			poly.verts[1] = i+1;
			poly.verts[2] = i+3;
			poly.verts[3] = i+2;
			if (context->lighting.ShadeModel == GL_FLAT)
			{
				W3D_Color col;
		    	col.r = context->VertexBuffer[i].r;
		    	col.g = context->VertexBuffer[i].g;
		    	col.b = context->VertexBuffer[i].b;
		    	col.a = context->VertexBuffer[i].a;
			    IWarp3D->W3D_SetCurrentColor(context->w3dContext, &col);
			}
			
			if (NEED_SPECIAL_FILL)
			{
				GLint fillMode = v_GetPolyFillMode(context, &poly);
				d_ClipAndDrawPolyWithFill(context, &poly, or_code, fillMode);
			}
			else
				hc_ClipAndDrawPoly(context, &poly, or_code);
		}
	}
}


void d_DrawQuads(GLcontext context)
{
	int i;
	GLuint or_code, and_code;
	ULONG error;
	
	v_Transform(context);

	for (i=0; i<context->VertexBufferPointer; i+=4)
	{
		hc_CodePoint(context, &(context->VertexBuffer[i]));
		hc_CodePoint(context, &(context->VertexBuffer[i+1]));
		hc_CodePoint(context, &(context->VertexBuffer[i+2]));
		hc_CodePoint(context, &(context->VertexBuffer[i+3]));

		or_code = context->VertexBuffer[i+0].outcode
				| context->VertexBuffer[i+1].outcode
				| context->VertexBuffer[i+2].outcode
				| context->VertexBuffer[i+3].outcode;

		and_code = context->VertexBuffer[i+0].outcode
				 & context->VertexBuffer[i+1].outcode
				 & context->VertexBuffer[i+2].outcode
				 & context->VertexBuffer[i+3].outcode;

		if (and_code) continue;

		if (hc_DecideFrontface(context, &(context->VertexBuffer[i]),
			&(context->VertexBuffer[i+1]),
			&(context->VertexBuffer[i+2]),or_code) == GL_FALSE) continue;

		if (or_code == 0 && !NEED_SPECIAL_FILL)
		{
			v_ToScreen(context, i);
			v_ToScreen(context, i+1);
			v_ToScreen(context, i+2);
			v_ToScreen(context, i+3);

			if (context->enable.Lighting)
			{
				GLfloat area = v_GetQuadArea(context, i, i+1, i+2, i+3);

				light_LightVertex(context, i, area);
				light_LightVertex(context, i+1, area);
				light_LightVertex(context, i+2, area);
				light_LightVertex(context, i+3, area);
			}

			if (context->lighting.ShadeModel == GL_FLAT)
			{
				SetFlatColor(context, 
							 context->VertexBuffer[i].r, context->VertexBuffer[i].g,
							 context->VertexBuffer[i].b, context->VertexBuffer[i].a);
			}
			
		    error = IWarp3D->W3D_DrawArray(context->w3dContext, 
			    	W3D_PRIMITIVE_TRIFAN, i, 4);

		}
		else
		{
			static MGLPolygon poly;
			poly.numverts = 4;
			poly.verts[0] = i;
			poly.verts[1] = i+1;
			poly.verts[2] = i+2;
			poly.verts[3] = i+3;
	
			if (context->lighting.ShadeModel == GL_FLAT)
			{
				SetFlatColor(context, 
							 context->VertexBuffer[i].r, context->VertexBuffer[i].g,
							 context->VertexBuffer[i].b, context->VertexBuffer[i].a);
			}
			if (NEED_SPECIAL_FILL)
			{
				GLint fillMode = v_GetPolyFillMode(context, &poly);
				d_ClipAndDrawPolyWithFill(context, &poly, or_code, fillMode);
			}
			else
			{
				hc_ClipAndDrawPoly(context, &poly, or_code);
			}
		}
	}
}

void d_DrawPolygon(GLcontext context)
{
	int i;
	GLuint or_code, and_code;
	MGLPolygon poly;

	v_Transform(context);
	or_code = 0; and_code = 0xFF;

	for (i=0; i<context->VertexBufferPointer; i++)
	{
		hc_CodePoint(context, &(context->VertexBuffer[i]));
		or_code  |= context->VertexBuffer[i].outcode;
		and_code &= context->VertexBuffer[i].outcode;
		poly.verts[i] = i;
	}

	if (and_code) 
		return;

	poly.numverts = context->VertexBufferPointer;

	if (or_code == 0 && !NEED_SPECIAL_FILL)
	{
		dh_DrawPolyFF(context, &poly);
	}
	else
	{
		if (NEED_SPECIAL_FILL)
		{
			for (i=0; i<context->VertexBufferPointer; i++)
			{
				v_ToScreen(context, i);
			}
			
			GLboolean isFrontFace = GL_TRUE;
			
			GLfloat area = dh_AreaSign(context, &poly);

			if (context->polygon.FrontFace == GL_CW) 
				area *= -1.f;


			if (area < context->MinTriArea) 
				isFrontFace = GL_FALSE;
			
			if (context->enable.CullFace)
			{
				switch(context->polygon.CullFace)
				{
				case GL_FRONT_AND_BACK: 
					return;
				case GL_FRONT:
					if (isFrontFace) return;
					break;
				case GL_BACK:
					if (!isFrontFace) return;
					break;
				}
			}
			
			GLint fillMode = v_GetFillMode(context, isFrontFace);
				
			d_ClipAndDrawPolyWithFill(context, &poly, or_code, fillMode);
		}
		else
		{
			hc_ClipAndDrawPolyFF(context, &poly, or_code);
		}
	}
}


void d_DrawTriangles(GLcontext context)
{
	int i;
	GLuint or_code, and_code;

/* FIXME: Use an approach similar to strips/fans: Gather as many triangles as
 * possible (i.e. visible, non-clipped) and draw them in one go
 */
	v_Transform(context);
	
	for (i=0; i<context->VertexBufferPointer; i+=3)
	{
		hc_CodePoint(context, &(context->VertexBuffer[i]));
		hc_CodePoint(context, &(context->VertexBuffer[i+1]));
		hc_CodePoint(context, &(context->VertexBuffer[i+2]));

		or_code = context->VertexBuffer[i+0].outcode
				| context->VertexBuffer[i+1].outcode
				| context->VertexBuffer[i+2].outcode;

		and_code = context->VertexBuffer[i+0].outcode
				 & context->VertexBuffer[i+1].outcode
				 & context->VertexBuffer[i+2].outcode;
		if (and_code) continue;

		if (hc_DecideFrontface(context, &(context->VertexBuffer[i]),
			&(context->VertexBuffer[i+1]),
			&(context->VertexBuffer[i+2]),or_code) == GL_FALSE) continue;
			
		if (or_code == 0 && !NEED_SPECIAL_FILL)
		{
			v_ToScreen(context, i);
			v_ToScreen(context, i+1);
			v_ToScreen(context, i+2);

			if (context->enable.Lighting)
			{
				GLfloat area = v_GetTriArea(context, i, i+1, i+2);

				light_LightVertex(context, i, area);
				light_LightVertex(context, i+1, area);
				light_LightVertex(context, i+2, area);
			}
		
			if (context->enable.PolygonOffsetFill)
			{
				GLfloat offset = v_PolygonOffset(context, i, i+1, i+2);
				context->VertexBuffer[i].z += offset;
				context->VertexBuffer[i+1].z += offset;
				context->VertexBuffer[i+2].z += offset;
			}
			
			IWarp3D->W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, i, 3);
		}
		else
		{
			static MGLPolygon poly;
			poly.numverts = 3;
			poly.verts[0] = i;
			poly.verts[1] = i+1;
			poly.verts[2] = i+2;

			if (context->enable.Lighting)
			{
				GLfloat area = v_GetTriArea(context, i, i+1, i+2);

				light_LightVertex(context, i, area);
				light_LightVertex(context, i+1, area);
				light_LightVertex(context, i+2, area);
			}

			if (context->enable.PolygonOffsetFill)
			{
				GLfloat offset = v_PolygonOffset(context, i, i+1, i+2);
				context->VertexBuffer[i].z += offset;
				context->VertexBuffer[i+1].z += offset;
				context->VertexBuffer[i+2].z += offset;
			}
			
			if (NEED_SPECIAL_FILL)
			{

				GLint fillMode = v_GetPolyFillMode(context, &poly);
				d_ClipAndDrawPolyWithFill(context, &poly, or_code, fillMode);
			}
			else
			{
				hc_ClipAndDrawPoly(context, &poly, or_code);
			}
		}
	}
}

/*
** A FlatFan is a triangle fan that is specified in device corrdinates and
** is drawn regardless of current matrix or other
*/
void d_DrawFlatFan(GLcontext context)
{
	ULONG error;
	int i;

	for (i=0; i<context->VertexBufferPointer; i++)
	{
		context->VertexBuffer[i].x = (W3D_Float) context->VertexBuffer[i].object.x;
		context->VertexBuffer[i].y = (W3D_Float) context->VertexBuffer[i].object.y;
		context->VertexBuffer[i].z = (W3D_Double)context->VertexBuffer[i].object.z;
		context->VertexBuffer[i].uvw[context->texture.ActiveTexture].w = 
									 (W3D_Float) context->VertexBuffer[i].object.w;
	}
	
	error = IWarp3D->W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN,
		0, context->VertexBufferPointer);
}

void dh_DrawPoly(GLcontext context, MGLPolygon *poly)
{
	int i;
	float area;
	GLfloat z[MGL_MAXVERTS];

	for (i=0; i<poly->numverts; i++)
		v_ToScreen(context, poly->verts[i]);
		
	area = dh_AreaSign(context, poly);
	if (fabs(area) < context->MinTriArea) 
		return;

	if (context->enable.Lighting)
	{
		for (i = 0; i < poly->numverts; i++)
			light_LightVertex(context, i, area);
	}
	
	if (context->enable.PolygonOffsetFill)
	{
		/* This is really bad! */
		for (i=0; i<poly->numverts; i++)
		{
			z[i] = context->VertexBuffer[poly->verts[i]].z;
			context->VertexBuffer[poly->verts[i]].z = min (1.0, context->VertexBuffer[poly->verts[i]].z + poly->zoffset);
		}
	}

	IWarp3D->W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_TRIFAN,
		W3D_INDEX_ULONG, poly->numverts, poly->verts);
		
	if (context->enable.PolygonOffsetFill)
	{
		for (i=0; i<poly->numverts; i++)
		{
			context->VertexBuffer[poly->verts[i]].z = z[i];
		}
	}
		
}

void dh_DrawPolyFF(GLcontext context, MGLPolygon *poly)
{
	int i;
	GLfloat area = 0.0;

	for (i=0; i<poly->numverts; i++)
		v_ToScreen(context, poly->verts[i]);

	if (context->enable.Lighting)
	{
		area = dh_AreaSign(context, poly);
		for (i = 0; i < poly->numverts; i++)
			light_LightVertex(context, i, area);
	}
	
	if (context->enable.CullFace == GL_TRUE)
	{
		if (!context->enable.Lighting)
			area = dh_AreaSign(context, poly);

		if (context->polygon.FrontFace == GL_CW) 
			area *= -1.f;

		if (area < context->MinTriArea) 
			return; // Either backfacing, or too small
	}
	else
	{
		area = dh_AreaSign(context, poly);
		if (fabs(area) < context->MinTriArea) 
			return;
	}

	IWarp3D->W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_TRIFAN,
		W3D_INDEX_ULONG, poly->numverts, poly->verts);
}


void cgl_GLRectf(struct GLContextIFace *Self, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
{
	Self->GLBegin(GL_POLYGON);
		Self->GLVertex4f(x1, y1, 0.0, 1.0);
		Self->GLVertex4f(x2, y1, 0.0, 1.0);
		Self->GLVertex4f(x2, y2, 0.0, 1.0);
		Self->GLVertex4f(x1, y2, 0.0, 1.0);
	Self->GLEnd();
}
