/*
 * $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[] = "$Id$";

#define DUMP_VERTEX(vert) \
 mykprintf("x:%6.3f y:%6.3f z:%6.3f w:%6.3f\nR:%6.3f G:%6.3f B:%6.3f A:%6.3f\nU:%6.3f V:%6.3f\noutcode=0x%x\n",\
		 vert.bx, vert.by, vert.bz, vert.bw,                                                                                      \
		 vert.v.color.r, vert.v.color.g, vert.v.color.b, vert.v.color.a,                                                          \
		 vert.v.u, vert.v.v, vert.outcode)

#define DUMP_VERTEX2(vert) \
 mykprintf("X:%6.3f Y:%6.3f Z:%6.3f W:%6.3f\nR:%6.3f G:%6.3f B:%6.3f A:%6.3f\nU:%6.3f V:%6.3f\n",\
		 vert.x, vert.y, vert.z, vert.w,                                                                                  \
		 vert.color.r, vert.color.g, vert.color.b, vert.color.a,                                                          \
		 vert.u, vert.v);                                                                                                         \


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);


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;
	extern void m_BuildInverted(GLcontext context);

	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)
{
	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)
	{
		for (i=0; i<context->VertexBufferPointer; i++)
		{
		v_ApplyMatrixElCheapo(context, &(context->CombinedMatrix), i);
		}
	}
	else
	{
		for (i=0; i<context->VertexBufferPointer; i++)
		{
		v_ApplyMatrixWOne(context, &(context->CombinedMatrix), i);
		}
	}

	}
}

INLINE void v_ToScreen(GLcontext context, int i)
{
	float 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;
	context->VertexBuffer[i].v.w = w;
}

void d_DrawPoints(GLcontext context)
{
}

void d_DrawLines(GLcontext context)
{
}

void d_DrawLineStrip(GLcontext context)
{
}

#if 0
void d_DumpPolygon(GLcontext context, MGLPolygon *poly)
{
	int i;
	mykprintf("d_DumpPolygon: %d vertices\n", poly->numverts);
	for (i=0; i<poly->numverts; i++)
	{
		DUMP_VERTEX(context->VertexBuffer[poly->verts[i]]);
	}
	mykprintf("-- \n");
}
#endif

void d_DrawTriangleFan(GLcontext context)
{
	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);
				if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);

			}
		}
	} while (i<context->VertexBufferPointer-1);

}

void d_DrawTriangleStrip(GLcontext context)
{
	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 (error != W3D_SUCCESS) kprintf("W3D_DrawTriStrip = %ld\n", error);

			}
		}
		if (context->CurrentFrontFace == GL_CCW) context->CurrentFrontFace = GL_CW;
		else                                     context->CurrentFrontFace = GL_CCW;
	} while (i<context->VertexBufferPointer - 2);

	context->CurrentFrontFace = CurrentFrontFace;

}

void d_DrawQuadStrip(GLcontext context)
{
	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);
			if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
		}
		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);
		}
	}
}


void d_DrawQuads(GLcontext context)
{
	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);
			if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
			#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
			#if 1
			if (error != W3D_SUCCESS) kprintf("[MiniGL::d_DrawQuads] Duh! Error %ld\n", error);
			#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);
		}
	}
}

void d_DrawPolygon(GLcontext context)
{
	int i;
	GLubyte or_code, and_code;
	MGLPolygon poly;
	extern void hc_ClipAndDrawPolyFF(GLcontext context, MGLPolygon *poly, GLubyte or_codes);

	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);
	}
}

void d_DrawTriangles(GLcontext context)
{
	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);
			if (error != W3D_SUCCESS) kprintf("W3D_DrawTriangle = %ld\n", error);
		}
		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);
		}
	}
}

/*
** 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);
	if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFanV = %ld\n", error);
	#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);
	if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
	#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);
	if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
	#endif
}

