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

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

void m_BuildInvertedFull(GLcontext context);
void v_ToEyeVertex(GLcontext context, MGLVertex *v);

#define LERP(t,a,b) \
	({ float aa = a; float bb = b; (aa) + (float)t * ( (bb) - (aa) ); })


#define CLIP_EPS (1e-7)

void hc_CodePoint(GLcontext context, MGLVertex *v)
{
	float w = v->clip.w;
	GLuint outcode = 0;

	if (v->clip.w < CLIP_EPS )
	{
		outcode |= MGL_CLIP_NEGW;
	}

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

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

	if (-w > v->clip.z)
	{
		outcode |= MGL_CLIP_BACK;
	}
	else if (v->clip.z > w)
	{
		outcode |= MGL_CLIP_FRONT;
	}
	
	if (context->transform.MaxClipPlane != -1)
	{
		int i;

		for (i = 0; i <= context->transform.MaxClipPlane; i++)
		{
			if (context->enable.ClipPlane[i])
			{
				GLfloat d 
				  = context->transform.UserClipPlane[i].eqn[0] * v->eye.x
				  + context->transform.UserClipPlane[i].eqn[1] * v->eye.y
				  + context->transform.UserClipPlane[i].eqn[2] * v->eye.z 
				  + context->transform.UserClipPlane[i].eqn[3] * v->eye.w;
					
				v->dist[i] = d;

				if (d >= 0.0)
					outcode |= context->transform.UserClipPlane[i].outcode;
			}
		}
	}

	v->outcode = outcode;
}

#define _x1 (a->clip.x)
#define _y1 (a->clip.y)
#define _z1 (a->clip.z)
#define _w1 (a->clip.w)

#define _x2 (b->clip.x)
#define _y2 (b->clip.y)
#define _z2 (b->clip.z)
#define _w2 (b->clip.w)

static void hc_ClipPlane(GLcontext context, GLint plane, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	float t = b->dist[plane] / (b->dist[plane] - a->dist[plane]);
	int i;	

	r->eye.x = LERP(t,a->eye.x, b->eye.x);
	r->eye.y = LERP(t,a->eye.y, b->eye.y);	
	r->eye.z = LERP(t,a->eye.z, b->eye.z);
	r->eye.w = LERP(t,a->eye.w, b->eye.w);
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

//    #define a(x) (context->CombinedMatrix.v[OF_##x])

    #define a(x) (CurrentP->v[OF_##x])
/*
	if (r->eye.w != 1.0)
	{
		r->eye.x /= r->eye.w;
		r->eye.y /= r->eye.w;
		r->eye.z /= r->eye.w;
		r->eye.w = 1.0;
	}
*/
	r->clip.x = a(11)*r->eye.x + a(12)*r->eye.y 
			+ a(13)*r->eye.z + a(14)*r->eye.w;
	r->clip.y = a(21)*r->eye.x + a(22)*r->eye.y 
			+ a(23)*r->eye.z + a(24)*r->eye.w;
	r->clip.z = a(31)*r->eye.x + a(32)*r->eye.y 
			+ a(33)*r->eye.z + a(34)*r->eye.w;
	r->clip.w = a(41)*r->eye.x + a(42)*r->eye.y 
			+ a(43)*r->eye.z + a(44)*r->eye.w;

	
	#undef a

	if (context->NeedEyeNormal)
		r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
				
	hc_CodePoint(context, r);
}

static void hc_ClipWZero(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1 = _w1, w2 = _w2;
	float t = (CLIP_EPS-w1)/(w2-w1);
	r->clip.x = LERP(t,a->clip.x, b->clip.x);
	r->clip.y = LERP(t,a->clip.y, b->clip.y);
	r->clip.z = LERP(t,a->clip.z, b->clip.z);
	r->clip.w = CLIP_EPS;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = CLIP_EPS;
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}

	hc_CodePoint(context, r);
}

static void hc_ClipTop(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1 = _w1, y1 = _y1, w2 = _w2, y2 = _y2;
	float t = (w1-y1)/((w1-y1)-(w2-y2));
	r->clip.x = LERP(t,a->clip.x, b->clip.x);
	r->clip.z = LERP(t,a->clip.z, b->clip.z);
	r->clip.w = LERP(t,a->clip.w, b->clip.w);
	r->clip.y = r->clip.w;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}
	
	hc_CodePoint(context, r);
}

static void hc_ClipBottom(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1 = _w1, y1 = _y1, w2 = _w2, y2 = _y2;
	float t = (w1+y1)/((w1+y1)-(w2+y2));
	r->clip.x = LERP(t,a->clip.x, b->clip.x);
	r->clip.z = LERP(t,a->clip.z, b->clip.z);
	r->clip.w = LERP(t,a->clip.w, b->clip.w);
	r->clip.y = -r->clip.w;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}
	
	hc_CodePoint(context, r);
}

static void hc_ClipLeft(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1=_w1, x1=_x1, w2=_w2, x2=_x2;
	float t = (w1+x1)/((w1+x1)-(w2+x2));
	r->clip.y = LERP(t,a->clip.y, b->clip.y);
	r->clip.z = LERP(t,a->clip.z, b->clip.z);
	r->clip.w = LERP(t,a->clip.w, b->clip.w);
	r->clip.x = -r->clip.w;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}

	hc_CodePoint(context, r);
}

static void hc_ClipRight(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1=_w1, x1=_x1, w2=_w2, x2=_x2;
	float t = (w1-x1)/((w1-x1)-(w2-x2));
	r->clip.y = LERP(t,a->clip.y, b->clip.y);
	r->clip.z = LERP(t,a->clip.z, b->clip.z);
	r->clip.w = LERP(t,a->clip.w, b->clip.w);
	r->clip.x = r->clip.w;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}
	
	hc_CodePoint(context, r);
}

static void hc_ClipFront(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1=_w1, z1=_z1, w2=_w2, z2=_z2;
	float t = (w1-z1)/((w1-z1)-(w2-z2));
	r->clip.x = LERP(t,a->clip.x, b->clip.x);
	r->clip.y = LERP(t,a->clip.y, b->clip.y);
	r->clip.w = LERP(t,a->clip.w, b->clip.w);
	r->clip.z = r->clip.w;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}
	
	hc_CodePoint(context, r);
}

static void hc_ClipBack(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *r)
{
	int i = 0;
	float w1=_w1,z1=_z1,w2=_w2,z2=_z2;
	float t = (w1+z1)/((w1+z1)-(w2+z2));
	r->clip.x = LERP(t,a->clip.x, b->clip.x);
	r->clip.y = LERP(t,a->clip.y, b->clip.y);
	r->clip.w = LERP(t,a->clip.w, b->clip.w);
	r->clip.z = -r->clip.w;
	r->a = LERP(t,a->a, b->a);
	r->r = LERP(t,a->r, b->r);
	r->g = LERP(t,a->g, b->g);
	r->b = LERP(t,a->b, b->b);

	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.Texture2D[i])
		{
			r->uvw[i].u = LERP(t,a->uvw[i].u, b->uvw[i].u);
			r->uvw[i].v = LERP(t,a->uvw[i].v, b->uvw[i].v);
			if (a->uvw[i].w == 1.0 && b->uvw[i].w == 1.0)
				r->uvw[i].w = 1.0;
			else
				r->uvw[i].w = LERP(t, a->uvw[i].w, b->uvw[i].w);
				
			r->rhw_valid[i] = a->rhw_valid[i];
		}
	}

	if (context->NeedEye)
	{
		r->eye.x = LERP(t, a->eye.x, b->eye.x);
		r->eye.y = LERP(t, a->eye.y, b->eye.y);
		r->eye.z = LERP(t, a->eye.z, b->eye.z);
		r->eye.w = 1.0;
		
		if (context->NeedEyeNormal)
			r->EyeNormal = a->EyeNormal;	// FIXME: incorrect
	}
	
	hc_CodePoint(context, r);
}

#undef x1
#undef y1
#undef z1
#undef w1
#undef x2
#undef y2
#undef z2
#undef w2

GLboolean hc_GetFrontFacing(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *c, GLuint outcode)
{
	GLboolean front;
	float a1,a2,b1,b2,r;
	float aw,bw,cw;
	
	aw = 1.0 / a->clip.w;
	bw = 1.0 / b->clip.w;
	cw = 1.0 / c->clip.w;

	#define EPSILON 1e-5

	a1 = a->clip.x*aw - b->clip.x*bw;
	a2 = a->clip.y*aw - b->clip.y*bw;
	b1 = c->clip.x*cw - b->clip.x*bw;
	b2 = c->clip.y*cw - b->clip.y*bw;
	r  = a1*b2-a2*b1;

	if (fabs(a1) < EPSILON && fabs(a2) < EPSILON)
		return GL_TRUE;

	if (fabs(b1) < EPSILON && fabs(b2) < EPSILON)
		return GL_TRUE;

	if ((r < 0.0 && context->polygon.FrontFace == GL_CCW) ||
		(r > 0.0 && context->polygon.FrontFace == GL_CW))
		front = GL_TRUE;
	else
		front = GL_FALSE;

	return front;
}


GLboolean hc_DecideFrontface(GLcontext context, MGLVertex *a, MGLVertex *b, MGLVertex *c, GLuint outcode)
{
	GLboolean front;
	float a1,a2,b1,b2,r;
	float aw,bw,cw;
	
	if (context->enable.CullFace == GL_FALSE)
		return GL_TRUE;
	if (context->polygon.CullFace == GL_FRONT_AND_BACK)
		return GL_FALSE;

	/*
	** The following line returns GL_TRUE if one or more of the vertices lie beyond the
	** camera plane. This is of course a wrong assumption, but those will be culled at
	** the clipping stage after the negative W coordinates have been removed.
	*/
	if (outcode&MGL_CLIP_NEGW)
		return GL_TRUE;
		
	aw = 1.0 / a->clip.w;
	bw = 1.0 / b->clip.w;
	cw = 1.0 / c->clip.w;

	#define EPSILON 1e-5

	a1 = a->clip.x*aw - b->clip.x*bw;
	a2 = a->clip.y*aw - b->clip.y*bw;
	b1 = c->clip.x*cw - b->clip.x*bw;
	b2 = c->clip.y*cw - b->clip.y*bw;
	r  = a1*b2-a2*b1;

	if (fabs(a1) < EPSILON && fabs(a2) < EPSILON)
		return GL_TRUE;

	if (fabs(b1) < EPSILON && fabs(b2) < EPSILON)
		return GL_TRUE;

	if ((r < 0.0 && context->polygon.FrontFace == GL_CCW) ||
		(r > 0.0 && context->polygon.FrontFace == GL_CW))
		front = GL_TRUE;
	else
		front = GL_FALSE;	
		
	if (context->polygon.CullFace == GL_BACK)
		return front;
	else
		return !front;
}


void EstablishCullState(GLcontext context)
{
	/* See if it's enabled at all */
	if (context->enable.CullFace == GL_FALSE)
	{
		/* No */
		return;
	}
	else
	{
		switch (context->polygon.CullFace)
		{
			case GL_FRONT_AND_BACK:
				/* Same as off */
				return;
			case GL_FRONT:
				/* Same as Warp3D */
				if (context->polygon.FrontFace == GL_CCW)
					IWarp3D->W3D_SetFrontFace(context->w3dContext, W3D_CW);
				else
					IWarp3D->W3D_SetFrontFace(context->w3dContext, W3D_CCW);
				break;
			case GL_BACK:
				/* Oposite of Warp3D */
				if (context->polygon.FrontFace == GL_CW)
					IWarp3D->W3D_SetFrontFace(context->w3dContext, W3D_CW);
				else
					IWarp3D->W3D_SetFrontFace(context->w3dContext, W3D_CCW);
				break;
		}
	}
}
				
		
void cgl_GLFrontFace(struct GLContextIFace *Self, GLenum mode)
{
	GLcontext context = GET_INSTANCE(Self);

	if (mode == GL_CW || mode == GL_CCW)
	{
		context->polygon.FrontFace = mode;
		EstablishCullState(context);
	}
	else
		GLFlagError(context, 1, GL_INVALID_ENUM);
}

void cgl_GLCullFace(struct GLContextIFace *Self, GLenum mode)
{
	GLcontext context = GET_INSTANCE(Self);

	if (mode == GL_FRONT || mode == GL_FRONT_AND_BACK || mode == GL_BACK)
	{
		context->polygon.CullFace = mode;
		EstablishCullState(context);
	}
	else
		GLFlagError(context, 1, GL_INVALID_ENUM);
}

/*
** Complicated clipping macro stuff.
*/

#define VERTP(i) &(context->VertexBuffer[a->verts[i]])
#define VERT(i) context->VertexBuffer[a->verts[i]]
#define POLYSWAP \
	if (b->numverts == 0) return; \
	temp=a; a=b; b=temp; \

#define POLYSWAP_DBG \
	if (b->numverts == 0) {IExec->DebugPrintF("nothing left\n"); return;} \
	temp=a; a=b; b=temp; \
	
#define USERPLANES 	(MGL_CLIP_USER0|MGL_CLIP_USER1|MGL_CLIP_USER2|MGL_CLIP_USER3|MGL_CLIP_USER4|MGL_CLIP_USER5|MGL_CLIP_USER6|MGL_CLIP_USER7)

#define DOCLIP(edge, routine)                                           \
	if (or_codes & edge)                                                 \
	{                                                                     \
		b->numverts = 0;                                                   \
		prev = a->numverts-1;                                               \
		for (i=0; i<a->numverts; i++)                                        \
		{                                                                     \
			/* Case 1 and 4*/                                                  \
			if (!(VERT(prev).outcode & edge))                                   \
			{                                                                    \
				b->verts[b->numverts] = a->verts[prev];                           \
				b->numverts++;                                                     \
			}                                                                       \
			/* Case 3 and 4 */                                                       \
			if ((VERT(prev).outcode ^ VERT(i).outcode) & edge)                        \
			{                                                                          \
				hc_##routine (context, VERTP(prev), VERTP(i), &(context->VertexBuffer[free]));   \
				b->verts[b->numverts]=free++;                                            \
				b->numverts++;                                                            \
			}                                                                              \
			prev = i;                                                                       \
		}                                                                                    \
		POLYSWAP                                                                              \
	}

#define DOCLIP_LINE(edge, routine)                                    \
	if (or_codes & edge)                                               \
	{                                                                   \
		b->numverts = 0;                                                 \
		prev = a->numverts-1; i=0;                                        \
		/* Case 1 and 4*/                                                  \
		if (!(VERT(prev).outcode & edge))                                   \
		{                                                                    \
			b->verts[b->numverts] = a->verts[prev];                           \
			b->numverts++;                                                     \
		}                                                                       \
		/* Case 3 and 4 */                                                       \
		if ((VERT(prev).outcode ^ VERT(i).outcode) & edge)                        \
		{                                                                          \
			hc_##routine (context, VERTP(prev), VERTP(i), &(context->VertexBuffer[free]));   \
			b->verts[b->numverts]=free++;                                            \
			b->numverts++;                                                            \
		}                                                                              \
		POLYSWAP                                                                        \
	}
	
#define DOCLIP_USER(edge, plane)									    \
	if (or_codes & edge)                                                 \
	{                                                                     \
		b->numverts = 0;                                                   \
		prev = a->numverts-1;                                               \
		for (i=0; i<a->numverts; i++)                                        \
		{                                                                     \
			/* Case 1 and 4*/                                                  \
			if (!(VERT(prev).outcode & edge))                                   \
			{                                                                    \
				b->verts[b->numverts] = a->verts[prev];                           \
				b->numverts++;                                                     \
			}                                                                       \
			/* Case 3 and 4 */                                                       \
			if ((VERT(prev).outcode ^ VERT(i).outcode) & edge)                        \
			{                                                                          \
				hc_ClipPlane(context, plane, VERTP(prev), VERTP(i), &(context->VertexBuffer[free]));   \
				b->verts[b->numverts]=free++;                                            \
				b->numverts++;                                                            \
			}                                                                              \
			prev = i;                                                                       \
		}                                                                                    \
		POLYSWAP                                                                              \
	}

#define DOCLIP_LINE_USER(edge, plane)	    					      \
	if (or_codes & edge)                                               \
	{                                                                   \
		b->numverts = 0;                                                 \
		prev = a->numverts-1; i=0;                                        \
		/* Case 1 and 4*/                                                  \
		if (!(VERT(prev).outcode & edge))                                   \
		{                                                                    \
			b->verts[b->numverts] = a->verts[prev];                           \
			b->numverts++;                                                     \
		}                                                                       \
		/* Case 3 and 4 */                                                       \
		if ((VERT(prev).outcode ^ VERT(i).outcode) & edge)                        \
		{                                                                          \
			hc_ClipPlane(context, plane, VERTP(prev), VERTP(i), &(context->VertexBuffer[free]));   \
			b->verts[b->numverts]=free++;                                            \
			b->numverts++;                                                            \
		}                                                                              \
		POLYSWAP                                                                        \
	}
	
extern void dh_DrawPoly(GLcontext context, MGLPolygon *poly);
extern void dh_DrawPolyFF(GLcontext context, MGLPolygon *poly);


void hc_ClipAndDrawPoly(GLcontext context, MGLPolygon *poly, GLuint or_codes)
{
	/*
	** Sutherland-Hodgeman clipping algorithm (almost)
	**
	** This clipping algorithm sucessively clips against each of the six
	** clipping planes (and, if enabled, user clipping planes).
	** Vertices are copied from a to b and swapped at the end.
	** Output occurs within the clipping region:
	**
	**				  |  b
	**                |/|
	**               i| |
	**               /| |
	**              / |/  c
	**            a/  |
	**             | /|j
	**             |/ |
	**            d/  |
	**                |clip plane
	**
	** In the above figure, the algorithm first consideres edge d-a. Since it
	** does not cross the clipping plane, it outputs d and proceeds to edge
	** a-b. Since it crosses, it outputs a, calculates intersection i and outputs it.
	** No output occurs while b-c is considered, since it lies outside the frustum
	** and does not cross it. Finally, edge c-d yields output j.
	**
	** The result is d-a-i-j
	**
	** Classification of edges are divided into four cases:
	** Case 1: Edge is completely inside -> two vertices
	** Case 2: Edge is completely outside -> no output
	** Case 3: Edge enters the frustum -> one output
	** Case 4: Edge leaves frustum -> two outputs
	**
	** At any stage, if the output polygon has zero vertices, return immediately.
	*/

	MGLPolygon output;
	MGLPolygon *a, *b, *temp;
	int i,j;
	int prev;
	int free = context->VertexBufferPointer;
	GLboolean flag;
	GLuint original_or_codes = or_codes;

	a = poly; b=&output;

	DOCLIP(MGL_CLIP_NEGW, ClipWZero);

	for (j=0;j<a->numverts; j++) 
		or_codes |= context->VertexBuffer[a->verts[j]].outcode;

	if (context->transform.MaxClipPlane != -1)
	{
		for (j = 0; j <= context->transform.MaxClipPlane; j++)
		{
			if (context->enable.ClipPlane[j])
			{
				DOCLIP_USER(context->transform.UserClipPlane[j].outcode, j);
			}
		}
	}
	

	DOCLIP(MGL_CLIP_LEFT, ClipLeft)
	DOCLIP(MGL_CLIP_RIGHT, ClipRight)
	DOCLIP(MGL_CLIP_FRONT, ClipFront)
	DOCLIP(MGL_CLIP_BACK, ClipBack)
	DOCLIP(MGL_CLIP_TOP, ClipTop)
	DOCLIP(MGL_CLIP_BOTTOM, ClipBottom)

	if ((original_or_codes & MGL_CLIP_NEGW) && a->numverts>2)
	{
		flag = hc_DecideFrontface(context,
			&(context->VertexBuffer[a->verts[0]]),
			&(context->VertexBuffer[a->verts[1]]),
			&(context->VertexBuffer[a->verts[2]]), 0);
		if (flag == GL_FALSE)
			return;
	}

	// If we get here, there are vertices left...		
	a->zoffset = poly->zoffset;
	dh_DrawPoly(context, a);
}

void hc_ClipAndDrawPolyFF(GLcontext context, MGLPolygon *poly, GLuint or_codes)
{
	MGLPolygon output;
	MGLPolygon *a, *b, *temp;
	int i,j;
	int prev;
	int free = context->VertexBufferPointer;

	a = poly; b=&output;

	DOCLIP(MGL_CLIP_NEGW, ClipWZero);

	for (j=0;j<a->numverts; j++) 
		or_codes |= context->VertexBuffer[a->verts[j]].outcode;

	if (context->transform.MaxClipPlane != -1)
	{
		for (j = 0; j <= context->transform.MaxClipPlane; j++)
		{
			if (context->enable.ClipPlane[j])
			{
				DOCLIP_USER(context->transform.UserClipPlane[j].outcode, j);
			}
		}
	}
	
	DOCLIP(MGL_CLIP_LEFT, ClipLeft)
	DOCLIP(MGL_CLIP_RIGHT, ClipRight)
	DOCLIP(MGL_CLIP_FRONT, ClipFront)
	DOCLIP(MGL_CLIP_BACK, ClipBack)
	DOCLIP(MGL_CLIP_TOP, ClipTop)
	DOCLIP(MGL_CLIP_BOTTOM, ClipBottom)

	// If we get here, there are vertices left...
	dh_DrawPolyFF(context, a);
}

#define LINE_CLIP(point, point2, plane, func) \
	if (point->outcode & plane) \
	{ \
		hc_##func(context, point, point2, r); \
		point = r; \
	}

extern void dh_DrawLine(GLcontext context, MGLPolygon *poly);

GLboolean hc_LineClip(GLcontext context, uint32 *pts)
{
	MGLVertex *p1 = &context->VertexBuffer[pts[0]];
	MGLVertex *p2 = &context->VertexBuffer[pts[1]];
	uint32 free = context->VertexBufferPointer;
	
	if (p1->outcode & p2->outcode)
	{
		// Trivial reject
		return GL_FALSE;
	}
	
	if ((p1->outcode | p2->outcode) == 0)
	{
		// Trivial accept
		return GL_TRUE;
	}
	
	if (p1->outcode)
	{
		// Point 1 is out of the frustum, clip to inside
		pts[0] = free;
		free++;
		MGLVertex *r = &context->VertexBuffer[pts[0]];
		LINE_CLIP(p1, p2, MGL_CLIP_NEGW, ClipWZero)
		LINE_CLIP(p1, p2, MGL_CLIP_LEFT, ClipLeft)
		LINE_CLIP(p1, p2, MGL_CLIP_RIGHT, ClipRight)
		LINE_CLIP(p1, p2, MGL_CLIP_FRONT, ClipFront)
		LINE_CLIP(p1, p2, MGL_CLIP_BACK, ClipBack)
		LINE_CLIP(p1, p2, MGL_CLIP_TOP, ClipTop)
		LINE_CLIP(p1, p2, MGL_CLIP_BOTTOM, ClipBottom)
	}
	
	if (p2->outcode)
	{
		// point 2 is out of the furstum
		pts[1] = free;
		MGLVertex *r = &context->VertexBuffer[pts[1]];
		LINE_CLIP(p2, p1, MGL_CLIP_NEGW, ClipWZero)
		LINE_CLIP(p2, p1, MGL_CLIP_LEFT, ClipLeft)
		LINE_CLIP(p2, p1, MGL_CLIP_RIGHT, ClipRight)
		LINE_CLIP(p2, p1, MGL_CLIP_FRONT, ClipFront)
		LINE_CLIP(p2, p1, MGL_CLIP_BACK, ClipBack)
		LINE_CLIP(p2, p1, MGL_CLIP_TOP, ClipTop)
		LINE_CLIP(p2, p1, MGL_CLIP_BOTTOM, ClipBottom)
	}
	return GL_TRUE;
}

// TODO: This was retrofitted to fit the old API, it would be better to 
//       get this done right to get right of the queer conversion stuff.
void hc_ClipAndDrawLine(GLcontext context, MGLPolygon *poly, GLuint or_codes)
{
	MGLPolygon p;
	uint32 pts[2];
	if (poly->numverts != 2) return; // Huh?
	
	pts[0] = poly->verts[0];
	pts[1] = poly->verts[1];
	if (GL_FALSE == hc_LineClip(context, pts)) return; // rejected
	p.numverts = 2;
	p.verts[0] = pts[0];
	p.verts[1] = pts[1];
	dh_DrawLine(context,&p);
}

#ifdef OBSOLETE_CODE
void hc_ClipAndDrawLine(GLcontext context, MGLPolygon *poly, GLuint or_codes)
{
    MGLPolygon output;
    MGLPolygon *a, *b, *temp;
    int i,j;
    int prev;
    int free = context->VertexBufferPointer;

    a = poly; b=&output;

    DOCLIP_LINE(MGL_CLIP_NEGW, ClipWZero);

    for (j = 0; j < a->numverts; j++)
		or_codes |= context->VertexBuffer[a->verts[j]].outcode;

    DOCLIP_LINE(MGL_CLIP_LEFT, ClipLeft)
    DOCLIP_LINE(MGL_CLIP_RIGHT, ClipRight)
    DOCLIP_LINE(MGL_CLIP_FRONT, ClipFront)
    DOCLIP_LINE(MGL_CLIP_BACK, ClipBack)
    DOCLIP_LINE(MGL_CLIP_TOP, ClipTop)
    DOCLIP_LINE(MGL_CLIP_BOTTOM, ClipBottom)

	if (context->MaxClipPlane != -1)
	{
		for (j = 0; j <= context->MaxClipPlane; j++)
		{
			if (context->UserClipPlane[j].enabled)
			{
				DOCLIP_LINE_USER(context->UserClipPlane[j].outcode, j);
			}
		}
	}

    dh_DrawLine(context,a);
}

void hc_ClipLine(GLcontext context, MGLPolygon *poly, GLuint or_codes,
	uint32 *out1, uint32 *out2)
{
    MGLPolygon output;
    MGLPolygon *a, *b, *temp;
    int i,j;
    int prev;
    int free = context->VertexBufferPointer;

    a = poly; b=&output;

    DOCLIP_LINE(MGL_CLIP_NEGW, ClipWZero);

    for (j = 0; j < a->numverts; j++)
		or_codes |= context->VertexBuffer[a->verts[j]].outcode;

    DOCLIP_LINE(MGL_CLIP_LEFT, ClipLeft)
    DOCLIP_LINE(MGL_CLIP_RIGHT, ClipRight)
    DOCLIP_LINE(MGL_CLIP_FRONT, ClipFront)
    DOCLIP_LINE(MGL_CLIP_BACK, ClipBack)
    DOCLIP_LINE(MGL_CLIP_TOP, ClipTop)
    DOCLIP_LINE(MGL_CLIP_BOTTOM, ClipBottom)

	if (context->MaxClipPlane != -1)
	{
		for (j = 0; j <= context->MaxClipPlane; j++)
		{
			if (context->UserClipPlane[j].enabled)
			{
				DOCLIP_LINE_USER(context->UserClipPlane[j].outcode, j);
			}
		}
	}

    if (out1) *out1 = a->verts[0];
    if (out2) *out2 = a->verts[1];
}
#endif

void cgl_GLClipPlane(struct GLContextIFace *Self, GLenum plane, GLdouble *eqn)
{
	GLcontext context = GET_INSTANCE(Self);

	int planeNum = plane - GL_CLIP_PLANE0;
	
	GLFlagError(context, plane < GL_CLIP_PLANE0, GL_INVALID_ENUM);
	GLFlagError(context, planeNum >= MAX_CLIPPLANES, GL_INVALID_ENUM);
	
	if (context->InverseModelViewValid != GL_TRUE)
		m_BuildInvertedFull(context);

	#define a(x) (context->InverseModelView.v[OF_##x])

	context->transform.UserClipPlane[planeNum].eqn[0] 
		= a(11)*eqn[0] + a(21)*eqn[1] + a(31)*eqn[2] + a(41)*eqn[3];
	context->transform.UserClipPlane[planeNum].eqn[1] 
		= a(12)*eqn[0] + a(22)*eqn[1] + a(32)*eqn[2] + a(42)*eqn[3];
	context->transform.UserClipPlane[planeNum].eqn[2] 
		= a(13)*eqn[0] + a(23)*eqn[1] + a(33)*eqn[2] + a(43)*eqn[3];
	context->transform.UserClipPlane[planeNum].eqn[3] 
		= a(14)*eqn[0] + a(24)*eqn[1] + a(34)*eqn[2] + a(44)*eqn[3];

//	kprintf("GL_CLIP_PLANE%d: <%f, %f, %f, %f>\n", (planeNum),
//		(float)context->UserClipPlane[planeNum].eqn[0],
//		(float)context->UserClipPlane[planeNum].eqn[1],
//		(float)context->UserClipPlane[planeNum].eqn[2],
//		(float)context->UserClipPlane[planeNum].eqn[3]);
		
	#undef a
}
