/*
 * $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>

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

#define DUMP_VERTEX(vert)

#define DUMP_VERTEX2(vert)


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, GLubyte or_codes);
extern void hc_ClipAndDrawLine(GLcontext context, MGLPolygon *poly, GLubyte or_codes);


INLINE void v_ApplyMatrixElCheapo(GLcontext context, Matrix *pA, int vnum)
{
	// El Cheapo implementation ATM
	#define a(x) (pA->v[OF_##x])
	float x=context->VertexBuffer[vnum].bx;
	float y=context->VertexBuffer[vnum].by;
	float z=context->VertexBuffer[vnum].bz;
	float w=context->VertexBuffer[vnum].bw;

	if (pA->flags == MGLMAT_IDENTITY) return;

	context->VertexBuffer[vnum].bx = a(11)*x + a(12)*y + a(13)*z + a(14)*w;
	context->VertexBuffer[vnum].by = a(21)*x + a(22)*y + a(23)*z + a(24)*w;
	context->VertexBuffer[vnum].bz = a(31)*x + a(32)*y + a(33)*z + a(34)*w;
	context->VertexBuffer[vnum].bw = a(41)*x + a(42)*y + a(43)*z + a(44)*w;

	return;
	#undef a
}


INLINE void v_ApplyMatrixWOne(GLcontext context, Matrix *pA, int vnum)
{
	// W assumed to be 1.0
	#define a(x) (pA->v[OF_##x])
	float x=context->VertexBuffer[vnum].bx;
	float y=context->VertexBuffer[vnum].by;
	float z=context->VertexBuffer[vnum].bz;

	context->VertexBuffer[vnum].bx = a(11)*x + a(12)*y + a(13)*z + a(14);
	context->VertexBuffer[vnum].by = a(21)*x + a(22)*y + a(23)*z + a(24);
	context->VertexBuffer[vnum].bz = a(31)*x + a(32)*y + a(33)*z + a(34);
	context->VertexBuffer[vnum].bw = a(41)*x + a(42)*y + a(43)*z + a(44);

	return;
	#undef a
}


/*
** 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,ry,rz;
	float m;
	float s,t;

	if (context->InvRotValid == GL_FALSE) m_BuildInverted(context);

	#define a(x)    (CurrentMV->v[OF_##x])
	#define b(x)    (CurrentP->v[OF_##x])
	#define verx  (context->VertexBuffer[vertex].bx)
	#define very  (context->VertexBuffer[vertex].by)
	#define verz  (context->VertexBuffer[vertex].bz)
	#define verw  (context->VertexBuffer[vertex].bw)

	u[0] = a(11)*verx + a(12)*very + a(13)*verz + a(14)*verw;
	u[1] = a(21)*verx + a(22)*very + a(23)*verz + a(24)*verw;
	u[2] = a(31)*verx + a(32)*very + a(33)*verz + a(34)*verw;
	u[3] = a(41)*verx + a(42)*very + a(43)*verz + a(44)*verw;

	verx = b(11)*u[0] + b(12)*u[1] + b(13)*u[2] + b(14)*u[3];
	very = b(21)*u[0] + b(22)*u[1] + b(23)*u[2] + b(24)*u[3];
	verz = b(31)*u[0] + b(32)*u[1] + b(33)*u[2] + b(34)*u[3];
	verw = b(41)*u[0] + b(42)*u[1] + b(43)*u[2] + b(44)*u[3];

	#undef a
	#undef b
	#undef verx
	#undef very
	#undef verz
	#undef verw

	#define ver(c)  (context->VertexBuffer[vertex].Normal.c)
	#define a(x)    (context->InvRot[x])

	// 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
	  

	if (u[3] != 1.f)
	{
		u[0] /= u[3]; u[1] /= u[3]; u[2] /= u[3];
	}

	// nomalize unit vector
	ul = (float)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;


	m = 0.5f / (float)sqrt((double)(rx*rx + ry*ry + (rz+1.f)*(rz+1.f)));

	s = rx*m + 0.5;
	t = ry*m + 0.5;

	if (context->TextureGenS_State == GL_TRUE)
	{
		context->VertexBuffer[vertex].v.u = context->w3dTexBuffer[context->CurrentBinding]->texwidth * s;
	}
	if (context->TextureGenT_State == GL_TRUE)
	{
		context->VertexBuffer[vertex].v.v = context->w3dTexBuffer[context->CurrentBinding]->texheight * t;
	}
}


INLINE void v_Transform(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;

	if (context->TextureGenS_State == GL_TRUE || context->TextureGenT_State == GL_TRUE)
	{
		for (i=0; i<context->VertexBufferPointer; i++)
		{
			v_GenTexCoords(context, i);
		}
	}
	else
	{
		if (context->CombinedValid == GL_FALSE)
		{
			m_CombineMatrices(context);
		}
		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->bx;
			float y = v->by;
			float z = v->bz;
			float w = v->bw;

			v->bx = a11*x + a12*y + a13*z + a14*w;
			v->by = a21*x + a22*y + a23*z + a24*w;
			v->bz = a31*x + a32*y + a33*z + a34*w;
			v->bw = a41*x + a42*y + a43*z + a44*w;
//                    v_ApplyMatrixElCheapo(context, &(context->CombinedMatrix), i);
		    }

		    #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->bx;
			    float y = v->by;
			    float z = v->bz;

			    v->bx = a11*x + a12*y + a13*z + a14;
			    v->by = a21*x + a22*y + a23*z + a24;
			    v->bz = a31*x + a32*y + a33*z + a34;
			    v->bw = a41*x + a42*y + a43*z + a44;
 //                    v_ApplyMatrixWOne(context, &(context->CombinedMatrix), i);
		    }
	       #undef a
	       }
	}
#else
// Does nothing
#endif
}

INLINE void v_ToScreen(GLcontext context, int i)
{
#if !defined(DISABLE_TRANSFORMATION)
	float w;

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

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

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

	if (context->VertexBuffer[i].q == 1.0)
		context->VertexBuffer[i].v.w = w;
	else
		context->VertexBuffer[i].v.w = context->VertexBuffer[i].q;
#else
	context->VertexBuffer[i].v.x = context->VertexBuffer[i].bx;
	context->VertexBuffer[i].v.y = context->VertexBuffer[i].by;
	context->VertexBuffer[i].v.z = 1.0 - context->VertexBuffer[i].bz;
	context->VertexBuffer[i].v.w = context->VertexBuffer[i].q;
#endif
}

void d_DrawPoints(GLcontext context)
{
}

void d_DrawLines(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
    int i;
    W3D_Line lin;
    GLubyte or_code, and_code;
    ULONG error;
    v_Transform(context);


    for (i=0; i<context->VertexBufferPointer; i+=2)
    {
	hc_CodePoint(&(context->VertexBuffer[i]));
	hc_CodePoint(&(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);
	    lin.v1 = (context->VertexBuffer[i+0].v);
	    lin.v2 = (context->VertexBuffer[i+1].v);
	    lin.tex = context->w3dTexBuffer[context->CurrentBinding];
	    lin.st_pattern = NULL;
	    lin.st_enable = 0;
	    lin.st_factor = 1;
	    error = W3D_DrawLine(context->w3dContext, &lin);
//            if (error != W3D_SUCCESS) kprintf("W3D_DrawTriangle = %ld\n",error);
	}
	else
	{
	    static MGLPolygon poly;
	    poly.numverts = 2;
	    poly.verts[0] = i;
	    poly.verts[1] = i+1;
	    hc_ClipAndDrawLine(context, &poly, or_code);
	}
    }
#else
    int i;
    W3D_Line lin;
    ULONG error;

    for (i=0;i<context->VertexBufferPointer; i+=2)
    {
	v_ToScreen(context,i);
	v_ToScreen(context,i+1);
	lin.v1 = (context->VertexBuffer[i+0].v);
	lin.v2 = (context->VertexBuffer[i+1].v);
	lin.tex = context->w3dTexBuffer[context->CurrentBinding];
	lin.st_pattern = NULL;
	lin.st_enable = 0;
	lin.st_factor = 1;
	error = W3D_DrawLine(context->w3dContext, &lin);
//        if (error != W3D_SUCCESS) kprintf("W3D_DrawTriangle = %ld\n", error);
    }
#endif
}

void dh_DrawLine(GLcontext context, MGLPolygon *poly)
{
    ULONG error;
    static W3D_Line lin;

    v_ToScreen(context, poly->verts[0]);
    lin.v1 = (context->VertexBuffer[poly->verts[0]].v);
    v_ToScreen(context, poly->verts[1]);
    lin.v2 = (context->VertexBuffer[poly->verts[1]].v);

    lin.tex = context->w3dTexBuffer[context->CurrentBinding];
    lin.st_pattern = NULL;
    lin.st_enable = 0;
    lin.st_factor  = 1;
    #ifndef NODRAW
    error = W3D_DrawLine(context->w3dContext, &lin);
//    if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
    #endif
}

void d_DrawLineStrip(GLcontext context)
{
}

void d_DrawTriangleFan(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;
	GLubyte and_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
	static W3D_TrianglesV tris;
	int triangle = 0;

	if (verts == NULL)
	{
		verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize);
		if (!verts) return;
	}

	v_Transform(context); // Transform world to clipping coordinates

	and_code = 0xFF;

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

	if (and_code) return;

	/*
	** 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;
				tris.vertexcount = 3;
				verts[0] = &(context->VertexBuffer[0].v);
				verts[1] = &(context->VertexBuffer[i].v);
				verts[2] = &(context->VertexBuffer[i+1].v);
				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)
				{
					verts[k] = &(context->VertexBuffer[i+1].v);
					v_ToScreen(context, i+1);
					i++; k++; triangle++;
					tris.vertexcount ++;
				}
				tris.tex = context->w3dTexBuffer[context->CurrentBinding];
				tris.st_pattern = NULL;
				tris.v = verts;
				error = W3D_DrawTriFanV(context->w3dContext, &tris);
			}
		}
	} while (i<context->VertexBufferPointer-1);

#else
	    {
		int i;
		ULONG error;
		static W3D_Vertex *verts[MGL_MAXVERTS];
		static W3D_TrianglesV tris;

		for (i=0; i<context->VertexBufferPointer; i++)
		{
			v_ToScreen(context, i);
			verts[i] = &(context->VertexBuffer[i].v);
		}

		tris.tex = context->w3dTexBuffer[context->CurrentBinding];
		tris.st_pattern = NULL;
		tris.vertexcount = context->VertexBufferPointer;
		tris.v = verts;
		#ifndef NODRAW
		error = W3D_DrawTriFanV(context->w3dContext, &tris);
		#endif
	    }
#endif
}

void d_DrawTriangleStrip(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;
	GLubyte and_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
	static W3D_TrianglesV tris;
	int triangle = 0;
	GLenum CurrentFrontFace = context->CurrentFrontFace;



	if (verts == NULL)
	{
		verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize);
		if (!verts) return;
	}

	v_Transform(context);

	and_code = 0xFF;
	for (i=0; i<context->VertexBufferPointer; i++)
	{
		hc_CodePoint(&(context->VertexBuffer[i]));
		and_code &= context->VertexBuffer[i].outcode;
	}

	if (and_code) return;

	/*
	** 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->CurrentFrontFace == GL_CCW) context->CurrentFrontFace = GL_CW;
		else                                     context->CurrentFrontFace = 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->CurrentFrontFace = 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;
				tris.vertexcount = 3;
				verts[0] = &(context->VertexBuffer[i+0].v);
				verts[1] = &(context->VertexBuffer[i+1].v);
				verts[2] = &(context->VertexBuffer[i+2].v);
				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)
				{
					verts[k] = &(context->VertexBuffer[i+2].v);
					v_ToScreen(context, i+2);
					i++; k++;
					triangle++;
					tris.vertexcount ++;
				}

				tris.tex = context->w3dTexBuffer[context->CurrentBinding];
				tris.st_pattern = NULL;
				tris.v = verts;
				error = W3D_DrawTriStripV(context->w3dContext, &tris);
			}
		}
		if (context->CurrentFrontFace == GL_CCW) context->CurrentFrontFace = GL_CW;
		else                                     context->CurrentFrontFace = GL_CCW;
	} while (i<context->VertexBufferPointer - 2);

	context->CurrentFrontFace = CurrentFrontFace;
#else
	    {
		int i;
		ULONG error;
		static W3D_Vertex *verts[MGL_MAXVERTS];
		static W3D_TrianglesV tris;

		for (i=0; i<context->VertexBufferPointer-1; i++)
		{
			v_ToScreen(context, i);
			verts[i] = &(context->VertexBuffer[i].v);
		}

		tris.tex = context->w3dTexBuffer[context->CurrentBinding];
		tris.st_pattern = NULL;
		tris.vertexcount = context->VertexBufferPointer-1;
		tris.v = verts;
		#ifndef NODRAW
		error = W3D_DrawTriStripV(context->w3dContext, &tris);
		#endif
	    }
#endif
}

void d_DrawQuadStrip(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;
	GLubyte or_code, and_code;
	ULONG error;
	static W3D_Vertex *verts[4];
	static W3D_TrianglesV tris;

	v_Transform(context);

	for (i=0; i<context->VertexBufferPointer-2; i+=2)
	{
		hc_CodePoint(&(context->VertexBuffer[i]));
		hc_CodePoint(&(context->VertexBuffer[i+1]));
		hc_CodePoint(&(context->VertexBuffer[i+2]));
		hc_CodePoint(&(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)
		{
			v_ToScreen(context, i);
			v_ToScreen(context, i+1);
			v_ToScreen(context, i+2);
			v_ToScreen(context, i+3);

			verts[0] = &(context->VertexBuffer[i+0].v);
			verts[1] = &(context->VertexBuffer[i+1].v);
			verts[2] = &(context->VertexBuffer[i+3].v);
			verts[3] = &(context->VertexBuffer[i+2].v);
			tris.tex = context->w3dTexBuffer[context->CurrentBinding];
			tris.st_pattern = NULL;
			tris.vertexcount = 4;
			tris.v = verts;
			if (context->ShadeModel == GL_FLAT)
			{
				W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
			}

			error = W3D_DrawTriFanV(context->w3dContext, &tris);
		}
		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->ShadeModel == GL_FLAT)
			{
				W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
			}
			hc_ClipAndDrawPoly(context, &poly, or_code);
		}
	}
#else
	int i;
	ULONG error;
	static W3D_Vertex *verts[4];
	static W3D_TrianglesV tris;

	for (i=0; i<context->VertexBufferPointer-2; i+=2)
	{
	    v_ToScreen(context, i);
	    v_ToScreen(context, i+1);
	    v_ToScreen(context, i+2);
	    v_ToScreen(context, i+3);

	    verts[0] = &(context->VertexBuffer[i+0].v);
	    verts[1] = &(context->VertexBuffer[i+1].v);
	    verts[2] = &(context->VertexBuffer[i+3].v);
	    verts[3] = &(context->VertexBuffer[i+2].v);
	    tris.tex = context->w3dTexBuffer[context->CurrentBinding];
	    tris.st_pattern = NULL;
	    tris.vertexcount = 4;
	    tris.v = verts;
	    if (context->ShadeModel == GL_FLAT)
	    {
		    W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
	    }

	    error = W3D_DrawTriFanV(context->w3dContext, &tris);
       }
#endif
}


void d_DrawQuads(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;
	GLubyte or_code, and_code;
	ULONG error;
	static W3D_Vertex *verts[4];
	static W3D_TrianglesV tris;


	v_Transform(context);

	for (i=0; i<context->VertexBufferPointer; i+=4)
	{
		hc_CodePoint(&(context->VertexBuffer[i]));
		hc_CodePoint(&(context->VertexBuffer[i+1]));
		hc_CodePoint(&(context->VertexBuffer[i+2]));
		hc_CodePoint(&(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->ShadeModel == GL_FLAT)
		{
			W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
		}

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

			verts[0] = &(context->VertexBuffer[i+0].v);
			verts[1] = &(context->VertexBuffer[i+1].v);
			verts[2] = &(context->VertexBuffer[i+2].v);
			verts[3] = &(context->VertexBuffer[i+3].v);
			tris.tex = context->w3dTexBuffer[context->CurrentBinding];
			tris.st_pattern = NULL;
			tris.vertexcount = 4;
			tris.v = verts;
			#ifndef NODRAW
			error = W3D_DrawTriFanV(context->w3dContext, &tris);
			#else
			printf("d_DrawQuads\n");
			DUMP_VERTEX(context->VertexBuffer[i+0]);
			DUMP_VERTEX(context->VertexBuffer[i+1]);
			DUMP_VERTEX(context->VertexBuffer[i+2]);
			DUMP_VERTEX(context->VertexBuffer[i+3]);
			printf("-----------------------------------\n");
			#endif
		}
		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;
			hc_ClipAndDrawPoly(context, &poly, or_code);
		}
	}

#else
	int i;
	ULONG error;
	static W3D_Vertex *verts[4];
	static W3D_TrianglesV tris;

	for (i=0; i<context->VertexBufferPointer; i+=4)
	{
	    v_ToScreen(context, i);
	    v_ToScreen(context, i+1);
	    v_ToScreen(context, i+2);
	    v_ToScreen(context, i+3);

	    verts[0] = &(context->VertexBuffer[i+0].v);
	    verts[1] = &(context->VertexBuffer[i+1].v);
	    verts[2] = &(context->VertexBuffer[i+2].v);
	    verts[3] = &(context->VertexBuffer[i+3].v);
	    tris.tex = context->w3dTexBuffer[context->CurrentBinding];
	    tris.st_pattern = NULL;
	    tris.vertexcount = 4;
	    tris.v = verts;
	    if (context->ShadeModel == GL_FLAT)
	    {
		    W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
	    }

	    error = W3D_DrawTriFanV(context->w3dContext, &tris);
       }
#endif
}

void d_DrawPolygon(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;
	GLubyte 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->VertexBuffer[i]));
		or_code  |= context->VertexBuffer[i].outcode;
		and_code &= context->VertexBuffer[i].outcode;
		poly.verts[i] = i;
	}


	if (and_code) return;
	#if 0
	if (hc_DecideFrontface(context, &(context->VertexBuffer[0]),
		&(context->VertexBuffer[1]),
		&(context->VertexBuffer[2]),or_code) == GL_FALSE) return;
	#endif

	poly.numverts = context->VertexBufferPointer;

	if (or_code == 0)
	{
		dh_DrawPolyFF(context, &poly);
	}
	else
	{
		hc_ClipAndDrawPolyFF(context, &poly, or_code);
	}
#else
	int i;
	ULONG error;
	static W3D_Vertex *verts[MGL_MAXVERTS];
	static W3D_TrianglesV tris;

	for (i=0; i<context->VertexBufferPointer; i++)
	{
	    v_ToScreen(context, i);
	    verts[i] = &(context->VertexBuffer[i].v);
	}

	tris.tex = context->w3dTexBuffer[context->CurrentBinding];
	tris.st_pattern = NULL;
	tris.vertexcount = context->VertexBufferPointer;
	tris.v = verts;
#ifndef NODRAW
	error = W3D_DrawTriFanV(context->w3dContext, &tris);
#endif

#endif
}

void d_DrawTriangles(GLcontext context)
{
#if !defined(DISABLE_TRANSFORMATION)
	int i;
	W3D_TriangleV tri;
	GLubyte or_code, and_code;
	ULONG error;

	v_Transform(context);

	for (i=0; i<context->VertexBufferPointer; i+=3)
	{
		hc_CodePoint(&(context->VertexBuffer[i]));
		hc_CodePoint(&(context->VertexBuffer[i+1]));
		hc_CodePoint(&(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)
		{
			v_ToScreen(context, i);
			v_ToScreen(context, i+1);
			v_ToScreen(context, i+2);

			tri.v1 = &(context->VertexBuffer[i+0].v);
			tri.v2 = &(context->VertexBuffer[i+1].v);
			tri.v3 = &(context->VertexBuffer[i+2].v);
			tri.tex = context->w3dTexBuffer[context->CurrentBinding];
			tri.st_pattern = NULL;
			error = W3D_DrawTriangleV(context->w3dContext, &tri);
		}
		else
		{
			static MGLPolygon poly;
			poly.numverts = 3;
			poly.verts[0] = i;
			poly.verts[1] = i+1;
			poly.verts[2] = i+2;
			hc_ClipAndDrawPoly(context, &poly, or_code);
		}
	}
#else
	int i;
	W3D_TriangleV tri;
	ULONG error;

	for (i=0; i<context->VertexBufferPointer; i+=3)
	{
		v_ToScreen(context, i);
		v_ToScreen(context, i+1);
		v_ToScreen(context, i+2);

		tri.v1 = &(context->VertexBuffer[i+0].v);
		tri.v2 = &(context->VertexBuffer[i+1].v);
		tri.v3 = &(context->VertexBuffer[i+2].v);
		tri.tex = context->w3dTexBuffer[context->CurrentBinding];
		tri.st_pattern = NULL;
		error = W3D_DrawTriangleV(context->w3dContext, &tri);
	}
#endif
}

/*
** 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;
	static W3D_Vertex *verts[MGL_MAXVERTS];
	static W3D_TrianglesV tris;

	for (i=0; i<context->VertexBufferPointer; i++)
	{
		verts[i] = &(context->VertexBuffer[i].v);
		context->VertexBuffer[i].v.x = (W3D_Float) context->VertexBuffer[i].bx;
		context->VertexBuffer[i].v.y = (W3D_Float) context->VertexBuffer[i].by;
		context->VertexBuffer[i].v.z = (W3D_Double)context->VertexBuffer[i].bz;
		context->VertexBuffer[i].v.w = (W3D_Float) context->VertexBuffer[i].bw;
	}
	tris.tex = context->w3dTexBuffer[context->CurrentBinding];
	tris.st_pattern = NULL;
	tris.v = verts;
	tris.vertexcount = context->VertexBufferPointer;
	#ifndef NODRAW
	error = W3D_DrawTriFanV(context->w3dContext, &tris);
	#endif
}

void dh_DrawPoly(GLcontext context, MGLPolygon *poly)
{
	int i;
	ULONG error;
	static W3D_Vertex *verts[MGL_MAXVERTS];
	static W3D_TrianglesV tris;

	for (i=0; i<poly->numverts; i++)
	{
		v_ToScreen(context, poly->verts[i]);
		verts[i] = &(context->VertexBuffer[poly->verts[i]].v);
	}

	tris.tex = context->w3dTexBuffer[context->CurrentBinding];
	tris.st_pattern = NULL;
	tris.vertexcount = poly->numverts;
	tris.v = verts;
	#ifndef NODRAW
	error = W3D_DrawTriFanV(context->w3dContext, &tris);
	#endif
}

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

	return area;
}

void dh_DrawPolyFF(GLcontext context, MGLPolygon *poly)
{
	int i;
	ULONG error;
	static W3D_Vertex *verts[MGL_MAXVERTS];
	static W3D_TrianglesV tris;
	GLfloat area;

	for (i=0; i<poly->numverts; i++)
	{
		v_ToScreen(context, poly->verts[i]);
		verts[i] = &(context->VertexBuffer[poly->verts[i]].v);
	}

	if (context->CullFace_State == GL_TRUE)
	{
		area = dh_AreaSign(context, poly);

		if (context->CurrentFrontFace == GL_CW) area *= -1.f;
		if (area < 0.0) return;
	}

	tris.tex = context->w3dTexBuffer[context->CurrentBinding];
	tris.st_pattern = NULL;
	tris.vertexcount = poly->numverts;
	tris.v = verts;
	#ifndef NODRAW
	error = W3D_DrawTriFanV(context->w3dContext, &tris);
	#endif
}

