/*
 * $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 <stdio.h>
#include <stdlib.h>
#include <string.h>

extern void d_DrawPoints(struct GLcontext_t);
extern void d_DrawLines(struct GLcontext_t);
extern void d_DrawLineStrip(struct GLcontext_t);
extern void d_DrawTriangles(struct GLcontext_t);
extern void d_DrawTriangleFan(struct GLcontext_t);
extern void d_DrawTriangleStrip(struct GLcontext_t);
extern void d_DrawQuads(struct GLcontext_t);
extern void d_DrawPolygon(struct GLcontext_t);
extern void d_DrawFlatFan(struct GLcontext_t);
extern void d_DrawQuadStrip(struct GLcontext_t);
extern void fog_Set(GLcontext);
extern void TMA_Start(LockTimeHandle *);
extern GLboolean TMA_Check(LockTimeHandle *);
extern float CLAMPF(float);
extern GLboolean MGLEnsureVertexBufferSize(GLcontext, GLuint);
extern void tex_EstablishEnvCombine(GLcontext);
extern void light_UpdateColorMaterial(GLcontext);
extern void light_UpdateColorMaterialVertex(GLcontext);
extern void GLDrawElementsTriangles(GLcontext, GLsizei, GLenum, const GLvoid *);
extern void v_GenTexCoords(GLcontext, int);
extern void v_ToEye(GLcontext, int);
extern void v_ToScreen(GLcontext, int);
extern void m_CombineMatrices(GLcontext);
GLfloat v_PolygonOffset(GLcontext context, int v1, int v2, int v3);
extern void v_GenEyeCoords(GLcontext);
extern void checkNeedEye(GLcontext context);

#define min(x,y) (x) < (y) ? (x) : (y)
#define max(x,y) (x) > (y) ? (x) : (y)

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

uint32 client_texture_state[] =
{
	GLCS_TEXTURE0,
	GLCS_TEXTURE1,
	GLCS_TEXTURE2,
	GLCS_TEXTURE3
};

/* Table for interleaved array */
struct ILAInfo
{
	GLboolean enable_color;
	GLboolean enable_normal;
	GLboolean enable_texture;
	GLuint vertex_size;
	GLuint vertex_offset;
	GLuint color_size;
	GLuint color_offset;
	GLuint texture_size;
	GLuint texture_offset;
	GLuint normal_offset;
	GLuint stride;
	GLenum color_format;
};

struct ILAInfo ILATab[] = 
{                   /*  color     normal   texture    vert    col     tex     n,   s   cf */
/* V2F             */ {GL_FALSE, GL_FALSE, GL_FALSE,  2, 0,   0, 0,   0, 0,   0,   8,  0},
/* V3F             */ {GL_FALSE, GL_FALSE, GL_FALSE,  3, 0,   0, 0,   0, 0,   0,   12, 0},
/* C4UB_V2F        */ {GL_TRUE,  GL_FALSE, GL_FALSE,  2, 4,   4, 0,   0, 0,   0,   12, GL_UNSIGNED_BYTE},
/* C4UB_V3F        */ {GL_TRUE,  GL_FALSE, GL_FALSE,  3, 4,   4, 0,   0, 0,   0,   16, GL_UNSIGNED_BYTE},
/* C3F_V3F         */ {GL_TRUE,  GL_FALSE, GL_FALSE,  3, 12,  3, 0,   0, 0,   0,   24, GL_FLOAT},
/* N3F_V3F         */ {GL_FALSE, GL_TRUE,  GL_FALSE,  3, 12,  0, 0,   0, 0,   0,   24, 0},
/* C4F_N3F_V3F     */ {GL_TRUE,  GL_TRUE,  GL_FALSE,  3, 24,  4, 0,   0, 0,   16,  40, GL_FLOAT}, 
/* T2F_V3F         */ {GL_FALSE, GL_FALSE, GL_TRUE,   3, 8,   0, 0,   2, 0,   0,   20, 0},
/* T4F_V4F         */ {GL_FALSE, GL_FALSE, GL_TRUE,   4, 16,  0, 0,   4, 0,   0,   32, 0},
/* T2F_C4UB_V3F    */ {GL_TRUE,  GL_FALSE, GL_TRUE,   3, 12,  4, 8,   2, 0,   0,   24, GL_UNSIGNED_BYTE}, 
/* T2F_C3F_V3F     */ {GL_TRUE,  GL_FALSE, GL_TRUE,   3, 20,  3, 20,  2, 0,   0,   32, GL_FLOAT},
/* T2F_N3F_V3F     */ {GL_FALSE, GL_TRUE,  GL_TRUE,   3, 20,  0, 0,   2, 0,   8,   32, 0},
/* T2F_C4F_N3F_V3F */ {GL_TRUE,  GL_TRUE,  GL_TRUE,   3, 36,  4, 8,   2, 0,   24,  48, GL_FLOAT},
/* T4F_C4F_N3F_V4F */ {GL_TRUE,  GL_TRUE,  GL_TRUE,   4, 44,  4, 16,  4, 0,   32,  60, GL_FLOAT}
};


/* These constants are used to build an index into an array of function pointers
 * that will be used to fetch certain combinations of size and amount of data.
 * So, a color fetch that would take 3 ubyte color component would be at index
 * FLAG_UBYTE|FLAG_3
 */
#define FLAG_BYTE		0x00
#define	FLAG_UBYTE		0x01
#define FLAG_SHORT		0x02
#define FLAG_USHORT		0x03
#define FLAG_INT		0x04
#define FLAG_UINT		0x05
#define FLAG_FLOAT		0x06
#define FLAG_DOUBLE		0x07
#define FLAG_1			0x10
#define FLAG_2			0x20
#define FLAG_3			0x30
#define FLAG_4			0x40
#define TABLE_SIZE		0x48

/* Arrays for color */
ArrayFetcherFn color_fetch[TABLE_SIZE];
ArrayFetcherIdxFn color_fetchIdx[TABLE_SIZE];
ArrayFetcherIdxFn color_fetchElement[TABLE_SIZE];

/* Color fetcher functions */
#define NAME(x) x##_byte
#define TYPE GLbyte
#define TYPEIDX FLAG_BYTE
#define DIVIDE 255.0
#include "colorfetch.h"

#define NAME(x) x##_ubyte
#define TYPE GLubyte
#define TYPEIDX FLAG_UBYTE
#define DIVIDE 255.0
#include "colorfetch.h"

#define NAME(x) x##_short
#define TYPE GLshort
#define TYPEIDX FLAG_SHORT
#define DIVIDE 65535.0
#include "colorfetch.h"

#define NAME(x) x##_ushort
#define TYPE GLushort
#define TYPEIDX FLAG_USHORT
#define DIVIDE 65535.0
#include "colorfetch.h"

#define NAME(x) x##_int
#define TYPE GLint
#define TYPEIDX FLAG_INT
#define DIVIDE 4294967295.0
#include "colorfetch.h"

#define NAME(x) x##_uint
#define TYPE GLuint
#define TYPEIDX FLAG_UINT
#define DIVIDE 4294967295.0
#include "colorfetch.h"

#define NAME(x) x##_float
#define TYPE GLfloat
#define TYPEIDX FLAG_FLOAT
#include "colorfetch.h"

#define NAME(x) x##_double
#define TYPE GLdouble
#define TYPEIDX FLAG_DOUBLE
#include "colorfetch.h"

void InitColorFetch(void)
{
	IUtility->ClearMem(color_fetch, sizeof(color_fetch));
	IUtility->ClearMem(color_fetchIdx, sizeof(color_fetchIdx));
	IUtility->ClearMem(color_fetchElement, sizeof(color_fetchElement));
	color_init_byte();
	color_init_ubyte();
	color_init_short();
	color_init_ushort();
	color_init_int();
	color_init_uint();
	color_init_float();
	color_init_double();
}


/* Arrays for texture coords */
ArrayFetcherFn texture_fetch[TABLE_SIZE];
ArrayFetcherIdxFn texture_fetchIdx[TABLE_SIZE];
ArrayFetcherIdxFn texture_fetchElement[TABLE_SIZE];

/* Texture fetcher functions */
#define NAME(x) x##_short
#define TYPE GLshort
#define TYPEIDX FLAG_SHORT
#include "tcoordfetch.h"

#define NAME(x) x##_int
#define TYPE GLint
#define TYPEIDX FLAG_INT
#include "tcoordfetch.h"

#define NAME(x) x##_float
#define TYPE GLfloat
#define TYPEIDX FLAG_FLOAT
#include "tcoordfetch.h"

#define NAME(x) x##_double
#define TYPE GLdouble
#define TYPEIDX FLAG_DOUBLE
#include "tcoordfetch.h"

void InitTextureFetch(void)
{
	IUtility->ClearMem(texture_fetch, sizeof(texture_fetch));
	IUtility->ClearMem(texture_fetchIdx, sizeof(texture_fetchIdx));
	IUtility->ClearMem(texture_fetchElement, sizeof(texture_fetchElement));
	texture_init_short();
	texture_init_int();
	texture_init_float();
	texture_init_double();
}


/* Arrays for vertex coords */
ArrayFetcherFn vertex_fetch[TABLE_SIZE];
ArrayFetcherIdxFn vertex_fetchIdx[TABLE_SIZE];
ArrayFetcherIdxFn vertex_fetchElement[TABLE_SIZE];

/* Vertex fetcher functions */
#define NAME(x) x##_short
#define TYPE GLshort
#define TYPEIDX FLAG_SHORT
#include "vertexfetch.h"

#define NAME(x) x##_int
#define TYPE GLint
#define TYPEIDX FLAG_INT
#include "vertexfetch.h"

#define NAME(x) x##_float
#define TYPE GLfloat
#define TYPEIDX FLAG_FLOAT
#include "vertexfetch.h"

#define NAME(x) x##_double
#define TYPE GLdouble
#define TYPEIDX FLAG_DOUBLE
#include "vertexfetch.h"

void InitVertexFetch(void)
{
	IUtility->ClearMem(vertex_fetch, sizeof(vertex_fetch));
	IUtility->ClearMem(vertex_fetchIdx, sizeof(vertex_fetchIdx));
	IUtility->ClearMem(vertex_fetchElement, sizeof(vertex_fetchElement));
	vertex_init_short();
	vertex_init_int();
	vertex_init_float();
	vertex_init_double();
}


/* Arrays for normal coords */
ArrayFetcherFn normal_fetch[TABLE_SIZE];
ArrayFetcherIdxFn normal_fetchIdx[TABLE_SIZE];
ArrayFetcherIdxFn normal_fetchElement[TABLE_SIZE];

/* normal fetcher functions */
#define NAME(x) x##_byte
#define TYPE GLbyte
#define TYPEIDX FLAG_BYTE
#include "normalfetch.h"

#define NAME(x) x##_short
#define TYPE GLshort
#define TYPEIDX FLAG_SHORT
#include "normalfetch.h"

#define NAME(x) x##_int
#define TYPE GLint
#define TYPEIDX FLAG_INT
#include "normalfetch.h"

#define NAME(x) x##_float
#define TYPE GLfloat
#define TYPEIDX FLAG_FLOAT
#include "normalfetch.h"

#define NAME(x) x##_double
#define TYPE GLdouble
#define TYPEIDX FLAG_DOUBLE
#include "normalfetch.h"

void InitNormalFetch(void)
{
	IUtility->ClearMem(normal_fetch, sizeof(normal_fetch));
	IUtility->ClearMem(normal_fetchIdx, sizeof(normal_fetchIdx));
	IUtility->ClearMem(normal_fetchElement, sizeof(normal_fetchElement));
	normal_init_byte();
	normal_init_short();
	normal_init_int();
	normal_init_float();
	normal_init_double();
}

void InitVertexArray(void)
{
	InitColorFetch();
	InitTextureFetch();
	InitNormalFetch();
	InitVertexFetch();
}

int sizeof_type(GLenum type)
{
	switch (type)
	{
		case GL_BYTE:
		case GL_UNSIGNED_BYTE:
			return 1;
		case GL_SHORT:
		case GL_UNSIGNED_SHORT:
			return 2;
		case GL_INT:
		case GL_UNSIGNED_INT:
		case GL_FLOAT:
			return 4;
		case GL_DOUBLE:
			return 8;
	}
	
	return 0;
}

GLuint type_index(GLenum type)
{
	switch (type)
	{
		case GL_BYTE:			return FLAG_BYTE;
		case GL_UNSIGNED_BYTE:	return FLAG_UBYTE;
		case GL_SHORT:			return FLAG_SHORT;
		case GL_UNSIGNED_SHORT:	return FLAG_USHORT;
		case GL_INT:			return FLAG_INT;
		case GL_UNSIGNED_INT:	return FLAG_UINT;
		case GL_FLOAT:			return FLAG_FLOAT;
		case GL_DOUBLE:			return FLAG_DOUBLE;
	}
	
	return 0;
}

GLuint size_index(GLint size)
{	
	switch (size)
	{
		case 1:					return FLAG_1;
		case 2:					return FLAG_2;
		case 3:					return FLAG_3;
		case 4:					return FLAG_4;
	}
	
	return 0;
}


void cgl_GLLockArrays(struct GLContextIFace *Self, GLint first, GLsizei count)
{
	GLcontext context = GET_INSTANCE(Self);

	context->LockedArrays = context->vertex_array.ClientState;
		
	context->LockArraysFirst = first;
	context->LockArraysCount = count;
	
	/* Ensure the vertex buffer is large enough. If not, this function will re-
	 * allocate it. If it can't do that, we can't use precompiled arrays...
	 */
	if (MGLEnsureVertexBufferSize(context, count-first))
	{
		context->FrameCode ++;
		context->TexFrameCode ++;
	}
	else
		context->LockedArrays = 0;
}

void cgl_GLUnlockArrays(struct GLContextIFace *Self)
{
	GLcontext context = GET_INSTANCE(Self);

	context->LockedArrays = 0;
}

void cgl_GLEnableClientState(struct GLContextIFace *Self, GLenum state)
{
	GLcontext context = GET_INSTANCE(Self);

	switch (state)
	{
	case GL_TEXTURE_COORD_ARRAY:
		GLFlagError(context, context->vertex_array.ClientActiveTexture >= MAX_TEXTURE_UNITS, 
				GL_INVALID_ENUM);
		context->vertex_array.ClientState |= client_texture_state[context->vertex_array.ClientActiveTexture];
		break;
	case GL_COLOR_ARRAY:
		context->vertex_array.ClientState |= GLCS_COLOR;
		break;
	case GL_VERTEX_ARRAY:
		context->vertex_array.ClientState |= GLCS_VERTEX;
		break;
	case GL_NORMAL_ARRAY:
		context->vertex_array.ClientState |= GLCS_NORMAL;
		break;
	default:
		GLFlagError(context, 1, GL_INVALID_ENUM);
		break;
	}
}


void cgl_GLDisableClientState(struct GLContextIFace *Self, GLenum state)
{
	GLcontext context = GET_INSTANCE(Self);

	switch (state)
	{
	case GL_TEXTURE_COORD_ARRAY:
		GLFlagError(context, context->vertex_array.ClientActiveTexture >= MAX_TEXTURE_UNITS, 
				GL_INVALID_ENUM);
		context->vertex_array.ClientState &= ~client_texture_state[context->vertex_array.ClientActiveTexture];
		break;
	case GL_COLOR_ARRAY:
		context->vertex_array.ClientState &= ~GLCS_COLOR;
		break;
	case GL_VERTEX_ARRAY:
		context->vertex_array.ClientState &= ~GLCS_VERTEX;
		break;
	case GL_NORMAL_ARRAY:
		context->vertex_array.ClientState &= ~GLCS_NORMAL;
		break;
	default:
		GLFlagError(context, 1, GL_INVALID_ENUM);
		break;
	}

}


void cgl_GLTexCoordPointer(struct GLContextIFace *Self, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
	GLcontext context = GET_INSTANCE(Self);
	GLuint active = context->vertex_array.ClientActiveTexture;
	GLuint index = type_index(type) | size_index(size);

	GLFlagError(context, context->vertex_array.ClientActiveTexture >= MAX_TEXTURE_UNITS, 
				GL_INVALID_ENUM);
	GLFlagError(context, size < 1 || size > 4, GL_INVALID_VALUE);
	GLFlagError(context, stride < 0, GL_INVALID_VALUE);

	context->TexFrameCode ++;
	
	if (stride == 0)
		stride = size*sizeof_type(type);

	context->vertex_array.TexCoordArray[active].size = size;
	context->vertex_array.TexCoordArray[active].type = type;
	context->vertex_array.TexCoordArray[active].stride = stride;
	context->vertex_array.TexCoordArray[active].pointer = (GLvoid *)pointer;
	
	context->vertex_array.TexCoordArray[active].fetch = texture_fetch[index];
	context->vertex_array.TexCoordArray[active].fetchIdx = texture_fetchIdx[index];
	context->vertex_array.TexCoordArray[active].fetchElement = texture_fetchElement[index];
	
	GLFlagError(context, context->vertex_array.TexCoordArray[active].fetch  == 0,
		GL_INVALID_ENUM);
}

void cgl_GLColorPointer(struct GLContextIFace *Self, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
	GLcontext context = GET_INSTANCE(Self);

	GLuint index = type_index(type) | size_index(size);
	
	GLFlagError(context, size < 3 || size > 4, GL_INVALID_VALUE);
	GLFlagError(context, stride < 0, GL_INVALID_VALUE);
	
	context->FrameCode ++;
	
	if (stride == 0)
		stride = size*sizeof_type(type);

	context->vertex_array.ColorArray.size = size;
	context->vertex_array.ColorArray.type = type;
	context->vertex_array.ColorArray.stride = stride;
	context->vertex_array.ColorArray.pointer = (GLvoid *)pointer;

	context->vertex_array.ColorArray.fetch = color_fetch[index];
	context->vertex_array.ColorArray.fetchIdx = color_fetchIdx[index];
	context->vertex_array.ColorArray.fetchElement = color_fetchElement[index];

	GLFlagError(context, context->vertex_array.ColorArray.fetch  == 0,
		GL_INVALID_ENUM);
}


void cgl_GLVertexPointer(struct GLContextIFace *Self, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
	GLcontext context = GET_INSTANCE(Self);

	GLuint index = type_index(type) | size_index(size);

	GLFlagError(context, size < 3 || size > 4, GL_INVALID_VALUE);
	GLFlagError(context, stride < 0, GL_INVALID_VALUE);
	
	context->FrameCode++;
	
	if (stride == 0)
		stride = size*sizeof_type(type);
		
	context->vertex_array.VertexArray.size = size;
	context->vertex_array.VertexArray.type = type;
	context->vertex_array.VertexArray.stride = stride;
	context->vertex_array.VertexArray.pointer = (GLvoid *)pointer;

	context->vertex_array.VertexArray.fetch = vertex_fetch[index];
	context->vertex_array.VertexArray.fetchIdx = vertex_fetchIdx[index];
	context->vertex_array.VertexArray.fetchElement = vertex_fetchElement[index];

	GLFlagError(context, context->vertex_array.VertexArray.fetch  == 0,
		GL_INVALID_ENUM);
}

void cgl_GLNormalPointer(struct GLContextIFace *Self, GLenum type, GLsizei stride, const GLvoid *pointer)
{
	GLcontext context = GET_INSTANCE(Self);

	GLuint index = type_index(type);

	GLFlagError(context, stride < 0, GL_INVALID_VALUE);
	
	context->FrameCode ++;
	
	if (stride == 0)
		stride = 3*sizeof_type(type);
		
	context->vertex_array.NormalArray.size = 3;
	context->vertex_array.NormalArray.type = type;
	context->vertex_array.NormalArray.stride = stride;
	context->vertex_array.NormalArray.pointer = (GLvoid *)pointer;

	context->vertex_array.NormalArray.fetch = normal_fetch[index];
	context->vertex_array.NormalArray.fetchIdx = normal_fetchIdx[index];
	context->vertex_array.NormalArray.fetchElement = normal_fetchElement[index];

	GLFlagError(context, context->vertex_array.NormalArray.fetch  == 0,
		GL_INVALID_ENUM);
}

void cgl_GLInterleavedArrays(struct GLContextIFace *Self, GLenum format, GLsizei stride, const GLvoid *pointer)
{
	GLcontext context = GET_INSTANCE(Self);
	
	GLint form = format - GL_V2F;
	GLubyte *base = (GLubyte *)pointer;

	GLFlagError(context, format < GL_V2F  || form > GL_T4F_C4F_N3F_V4F, 
			GL_INVALID_ENUM)
	GLFlagError(context, stride < 0, GL_INVALID_VALUE);
	
	context->FrameCode ++;
	
	context->vertex_array.ClientState = GLCS_VERTEX;
	
	if (stride == 0)
		stride = ILATab[form].stride;

	if (ILATab[form].enable_color)
	{
		context->vertex_array.ClientState |= GLCS_COLOR;
		Self->GLColorPointer(ILATab[form].color_size, ILATab[form].color_format,
				stride, base + ILATab[form].color_offset);
	}
	
	if (ILATab[form].enable_normal)
	{
		context->vertex_array.ClientState |= GLCS_NORMAL;
		Self->GLNormalPointer(GL_FLOAT, stride, 
				base + ILATab[form].normal_offset);
	}
	
	if (ILATab[form].enable_texture)
	{
		context->TexFrameCode ++;
		context->vertex_array.ClientState |= GLCS_TEXTURE0;
		Self->GLTexCoordPointer(ILATab[form].texture_size, GL_FLOAT, 
				stride, base + ILATab[form].texture_offset);
	}
	
	Self->GLVertexPointer(ILATab[form].vertex_size, GL_FLOAT,
				stride, base + ILATab[form].vertex_offset);

}

void cgl_GLClientActiveTexture(struct GLContextIFace *Self, GLenum texture)
{
	GLcontext context = GET_INSTANCE(Self);

	texture -= GL_TEXTURE0;
	if (texture < MAX_TEXTURE_UNITS)
		context->vertex_array.ClientActiveTexture = texture;
	else
		GLFlagError(context, 1, GL_INVALID_ENUM);
}

static inline GLubyte *ArrayAddress(GLarray *array, int elem)
{
	return (GLubyte *)array->pointer + array->stride * elem;
}

void cgl_GLArrayElement(struct GLContextIFace *Self, GLint i)
{
	GLcontext context = GET_INSTANCE(Self);

	int t;
	
	if (context->vertex_array.ClientState & GLCS_COLOR 
	&& context->vertex_array.ColorArray.fetchElement)
		context->vertex_array.ColorArray.fetchElement(context, 
			&context->vertex_array.ColorArray, i, 0);

	for (t = 0; t <= context->texture.MaxTextureUnit; t++)
	{
		if (context->vertex_array.ClientState & client_texture_state[t] 
		 && context->enable.Texture2D[t]
		 && context->vertex_array.TexCoordArray[t].fetchElement)
			context->vertex_array.TexCoordArray[t].fetchElement(context, 
					&context->vertex_array.TexCoordArray[t], i, t);
	}

	if (context->vertex_array.ClientState & GLCS_NORMAL 
	&& context->vertex_array.NormalArray.fetchElement)
		context->vertex_array.NormalArray.fetchElement(context,
					 &context->vertex_array.NormalArray, i, 0);
	
	/* Note: This will also advance the vertex array pointer */
	if (context->vertex_array.ClientState & GLCS_VERTEX 
	&& context->vertex_array.VertexArray.fetchElement)
		context->vertex_array.VertexArray.fetchElement(context, 
			&context->vertex_array.VertexArray, i, 0);

}

static GLboolean ArrayBegin(GLcontext context, GLenum mode)
{	
	if (!context->LockedArrays)
	{
		context->FrameCode ++;
		context->TexFrameCode ++;
	}
	
	context->VertexBufferPointer = 0;
	
	tex_EstablishEnvCombine(context);
	checkNeedEye(context);
		
	switch((int)mode)
	{
		case GL_POINTS:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawPoints;
			break;
		case GL_LINES:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawLines;
			break;
		case GL_LINE_STRIP:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawLineStrip;
			break;
		case GL_LINE_LOOP:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawLineStrip;
			break;
		case GL_TRIANGLES:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawTriangles;
			break;
		case GL_TRIANGLE_STRIP:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawTriangleStrip;
			break;
		case GL_TRIANGLE_FAN:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawTriangleFan;
			break;
		case GL_QUADS:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawQuads;
			break;
		case GL_QUAD_STRIP:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawQuadStrip;
			break;
		case GL_POLYGON:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawPolygon;
			break;
		case MGL_FLATFAN:
			context->CurrentPrimitive = mode;
			context->CurrentDraw = (DrawFn)d_DrawFlatFan;
			break;
		default:
			GLFlagError2(context, 1, GL_INVALID_OPERATION, GL_FALSE);
	}	

	return GL_TRUE;
}

void MaybeLock(GLcontext context)
{	
	tex_EstablishEnvCombine(context);

	if (context->FogDirty && context->enable.Fog)
	{
		fog_Set(context);
		context->FogDirty = GL_FALSE;
	}

	if (context->lighting.ShadeModel == GL_FLAT)
	{
		static W3D_Color color;
		color.r = CLAMPF(context->current.CurrentColor.r);
		color.g = CLAMPF(context->current.CurrentColor.g);
		color.b = CLAMPF(context->current.CurrentColor.b);
		color.a = CLAMPF(context->current.CurrentColor.a);
		IWarp3D->W3D_SetCurrentColor(context->w3dContext, &color);
	}
	
#ifdef AUTOMATIC_LOCKING_ENABLE
	if (context->LockMode == MGL_LOCK_MANUAL) // Manual: Lock manually
	{
		IWarp3D->W3D_InterleavedArray(context->w3dContext, 
				context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat,
				W3D_TEXCOORD_NORMALIZED);
	}
	else // Smart: Lock timer based
	{
		if (context->w3dLocked == GL_FALSE)
		{
			if (W3D_SUCCESS != IWarp3D->W3D_LockHardware(context->w3dContext))
			{
				dprintf("[glEnd] Error during W3D_LockHardware()\n");
				return; // give up
			}
			context->w3dLocked = GL_TRUE;
			TMA_Start(&(context->LockTime));
		}

		IWarp3D->W3D_InterleavedArray(context->w3dContext, 
				context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat,
				W3D_TEXCOORD_NORMALIZED);
	}
#endif	
}


void MaybeUnlock(GLcontext context)
{
	if (context->LockMode == MGL_LOCK_SMART)
	{
		if (TMA_Check(&(context->LockTime)) == GL_TRUE)
		{
			// Time to unlock
			IWarp3D->W3D_UnLockHardware(context->w3dContext);
			context->w3dLocked = GL_FALSE;
		}
	}
}

static void ArrayEnd(GLcontext context)
{
	if (context->FogDirty && context->enable.Fog)
	{
		fog_Set(context);
		context->FogDirty = GL_FALSE;
	}

	if (context->lighting.ShadeModel == GL_FLAT)
	{
		static W3D_Color color;
		color.r = CLAMPF(context->current.CurrentColor.r);
		color.g = CLAMPF(context->current.CurrentColor.g);
		color.b = CLAMPF(context->current.CurrentColor.b);
		color.a = CLAMPF(context->current.CurrentColor.a);
		IWarp3D->W3D_SetCurrentColor(context->w3dContext, &color);
	}

	// Check for blending inconsistancy
//	if (context->AlphaFellBack && (context->SrcAlpha == GL_ONE || context->DstAlpha == GL_ONE)
//		&& context->Blend_State == GL_TRUE)
//	{
//		tex_ConvertTexture(context);
//	}


#ifdef AUTOMATIC_LOCKING_ENABLE
	if (context->LockMode == MGL_LOCK_AUTOMATIC) // Automatic: Lock per primitive
	{
		if (W3D_SUCCESS == IWarp3D->W3D_LockHardware(context->w3dContext))
		{
			context->w3dLocked = GL_TRUE;
			IWarp3D->W3D_InterleavedArray(context->w3dContext, 
				context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat,
				W3D_TEXCOORD_NORMALIZED);
			context->CurrentDraw(context);
			IWarp3D->W3D_UnLockHardware(context->w3dContext);
			context->w3dLocked = GL_FALSE;
		}
		else
		{
			dprintf("Error during LockHardware\n");
		}
	}
	else if (context->LockMode == MGL_LOCK_MANUAL) // Manual: Lock manually
	{
		IWarp3D->W3D_InterleavedArray(context->w3dContext, 
				context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat,
				W3D_TEXCOORD_NORMALIZED);
		context->CurrentDraw(context);
	}
	else // Smart: Lock timer based
	{
		if (context->w3dLocked == GL_FALSE)
		{
			if (W3D_SUCCESS != IWarp3D->W3D_LockHardware(context->w3dContext))
			{
				dprintf("[glEnd] Error during W3D_LockHardware()\n");
				return; // give up
			}
			context->w3dLocked = GL_TRUE;
			TMA_Start(&(context->LockTime));
		}

		IWarp3D->W3D_InterleavedArray(context->w3dContext, 
				context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat,
				W3D_TEXCOORD_NORMALIZED);
		context->CurrentDraw(context);  // Draw!
		if (TMA_Check(&(context->LockTime)) == GL_TRUE)
		{
			// Time to unlock
			IWarp3D->W3D_UnLockHardware(context->w3dContext);
			context->w3dLocked = GL_FALSE;
		}
	}
#else
	IWarp3D->W3D_InterleavedArray(context->w3dContext, 
		context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat, 
		W3D_TEXCOORD_NORMALIZED);
		
	context->CurrentDraw(context);
#endif
	context->CurrentPrimitive = GL_BASE;
}

#define thisvertex context->VertexBuffer[context->VertexBufferPointer]

void CurrentColorFetch(GLcontext context, struct GLarray_t *array, GLvoid *cur, GLuint unit)
{
	thisvertex.r = context->current.CurrentColor.r;
	thisvertex.g = context->current.CurrentColor.g;
	thisvertex.b = context->current.CurrentColor.b;
	thisvertex.a = context->current.CurrentColor.a;
#ifdef MGL_USE_LIGHTING
	if (context->enable.ColorMaterial)
		light_UpdateColorMaterialVertex(context);
#endif	
}

void CurrentNormalFetch(GLcontext context, struct GLarray_t *array, GLvoid *cur, GLuint unit)
{
	thisvertex.Normal.x = context->current.CurrentNormal.x;
	thisvertex.Normal.y = context->current.CurrentNormal.y;
	thisvertex.Normal.z = context->current.CurrentNormal.z;
}

void CurrentTextureFetch(GLcontext context, struct GLarray_t *array, GLvoid *cur, GLuint i)
{
	if (context->current.CurTexQValid[i] == GL_TRUE)
	{
		float oneoverq = 1.0 / context->current.CurTexQ[i];
		thisvertex.uvw[i].u = context->current.CurTexS[i] * oneoverq;
		thisvertex.uvw[i].v = context->current.CurTexT[i] * oneoverq;
		thisvertex.uvw[i].w = context->current.CurTexQ[i];
		thisvertex.rhw_valid[i] = GL_TRUE;
	}
	else
	{
		thisvertex.uvw[i].u = context->current.CurTexS[i];
		thisvertex.uvw[i].v = context->current.CurTexT[i];
		thisvertex.uvw[i].w = 1.0;
		thisvertex.rhw_valid[i] = GL_FALSE;
	}
}

#undef thisvertex

void cgl_GLDrawArrays(struct GLContextIFace *Self, GLenum mode, GLint first, GLsizei count)
{
	GLcontext context = GET_INSTANCE(Self);

	int i;
	int c;
	int full_packs;
	int part_verts;
	int pack_size = context->VertexBufferSize;
	MGLVertex t1, t2;	
	int verts_to_copy = 0;
	int pass = 0;
	GLubyte 
		*color = 0,
		*vertex = 0,
		*normal = 0,
		*texture[MAX_TEXTURE_UNITS] = {0, };
	
	ArrayFetcherFn 
		color_fetch = 0, 
		vertex_fetch = 0, 
		normal_fetch = 0, 
		texture_fetch[MAX_TEXTURE_UNITS] = {0, };
	
	GLFlagError(context, first < 0, GL_INVALID_VALUE);
	
	/* Some special handling for certain primitive types */	
	switch (mode)
	{
		case GL_TRIANGLES:
			pack_size = pack_size - (pack_size % 3);
			break;
		case GL_QUADS:
			pack_size = pack_size - (pack_size % 4);
			break;
		case GL_TRIANGLE_FAN:
		case MGL_FLATFAN:
			verts_to_copy = 1;
			break;
		case GL_TRIANGLE_STRIP:
		case GL_QUAD_STRIP:
			verts_to_copy = 2;
			break;
	}
	
	full_packs = count / pack_size;
	part_verts = count % pack_size;
	
	/* Setup the array and function pointers(if active) */
	if (context->vertex_array.ClientState & GLCS_COLOR)
	{
		color_fetch = context->vertex_array.ColorArray.fetch;
		color = ArrayAddress(&context->vertex_array.ColorArray, first);
		GLFlagError(context, color_fetch == 0, GL_INVALID_ENUM);
	}
	else
		color_fetch = CurrentColorFetch;
		
	if (context->vertex_array.ClientState & GLCS_NORMAL)
	{
		normal_fetch = context->vertex_array.NormalArray.fetch;
		normal = ArrayAddress(&context->vertex_array.NormalArray, first);
		GLFlagError(context, normal_fetch == 0, GL_INVALID_ENUM);		
	}
	else
		normal_fetch = CurrentNormalFetch;	
		
	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->vertex_array.ClientState & client_texture_state[i] 
		&& context->enable.Texture2D[i])
		{
			texture_fetch[i] = context->vertex_array.TexCoordArray[i].fetch;
			texture[i] = ArrayAddress(&context->vertex_array.TexCoordArray[i], 
											first);
			GLFlagError(context, texture_fetch[i] == 0, GL_INVALID_ENUM);
		}
		else
			texture_fetch[i] = CurrentTextureFetch;
	}
	
	if (context->vertex_array.ClientState & GLCS_VERTEX)
	{
		vertex_fetch = context->vertex_array.VertexArray.fetch;
		vertex = ArrayAddress(&context->vertex_array.VertexArray, first);
	}

	GLFlagError(context, vertex_fetch == 0, GL_INVALID_ENUM);

	/* Start drawing */		
	while (full_packs--)
	{
		if (GL_FALSE == ArrayBegin(context, mode))
			return;		
		
		/* On subsequent passes, strips and fans need "key" vertices copied */
		if (pass != 0)
		{
			if (verts_to_copy == 1)
			{
				context->VertexBuffer[0] = t1;
				context->VertexBufferPointer = 1;
			}
			else if (verts_to_copy == 2)
			{
				context->VertexBuffer[0] = t1;
				context->VertexBuffer[1] = t2;
				context->VertexBufferPointer = 2;
			}
		}
		
		for (c = 0; c < pack_size; c++)
		{
			color_fetch(context, &context->vertex_array.ColorArray, color, 0);
			color += context->vertex_array.ColorArray.stride;
			
			normal_fetch(context, &context->vertex_array.NormalArray, normal, 0);
			normal += context->vertex_array.NormalArray.stride;
			
			for (i = 0; i <= context->texture.MaxTextureUnit; i++)
			{
				if (context->enable.Texture2D[i])
				{
					texture_fetch[i](context, 
							&context->vertex_array.TexCoordArray[i], texture[i],
							i);
					texture[i] += context->vertex_array.TexCoordArray[i].stride;
				}
			}
			
			vertex_fetch(context, &context->vertex_array.VertexArray, vertex, 0);
			vertex += context->vertex_array.VertexArray.stride;
			
			context->VertexBufferPointer ++;
		}
		
		pass++;
		
		if (verts_to_copy == 1)
		{
			t1 = context->VertexBuffer[0];
		}
		else if (verts_to_copy == 2)
		{
			t1 = context->VertexBuffer[context->VertexBufferPointer-2];
			t2 = context->VertexBuffer[context->VertexBufferPointer-1];
		}
		
		ArrayEnd(context);
	}
	
	/* Draw the rest which didn't fit the full packages */
	if (part_verts)
	{
		if (GL_FALSE == ArrayBegin(context, mode))
			return;		
	
		for (c = 0; c < part_verts; c++)
		{
			color_fetch(context, &context->vertex_array.ColorArray, color, 0);
			color += context->vertex_array.ColorArray.stride;
			
			normal_fetch(context, &context->vertex_array.NormalArray, normal, 0);
			normal += context->vertex_array.NormalArray.stride;
			
			for (i = 0; i <= context->texture.MaxTextureUnit; i++)
			{
				if (context->enable.Texture2D[i])
				{
					texture_fetch[i](context, 
						&context->vertex_array.TexCoordArray[i], texture[i], i);
					texture[i] += context->vertex_array.TexCoordArray[i].stride;
				}
			}
			
			vertex_fetch(context, &context->vertex_array.VertexArray, vertex, 0);
			vertex += context->vertex_array.VertexArray.stride;
			
			context->VertexBufferPointer ++;
		}	
		
		ArrayEnd(context);
	}
}


GLuint FetchElementByte(GLvoid **indices)
{
	GLuint ret;
	GLubyte *index = (GLubyte *) *indices;
	
	ret = *index++;
	
	*indices = (GLvoid *)index;
	
	return ret;
}

GLuint FetchElementShort(GLvoid **indices)
{
	GLuint ret;
	GLushort *index = (GLushort *) *indices;
	
	ret = *index++;
	
	*indices = (GLvoid *)index;
	
	return ret;
}

GLuint FetchElementInt(GLvoid **indices)
{
	GLuint ret;
	GLuint *index = (GLuint *) *indices;
	
	ret = *index++;
	
	*indices = (GLvoid *)index;
	
	return ret;
}
	
void cgl_GLDrawElements(struct GLContextIFace *Self, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
{
	GLcontext context = GET_INSTANCE(Self);

	int i;
	int c;
	int full_packs;
	int part_verts;
	int pack_size = context->VertexBufferSize;
	MGLVertex t1, t2;	
	int verts_to_copy = 0;
	int pass = 0;
	ArrayFetcherIdxFn 
		color_fetch = 0, 
		vertex_fetch = 0, 
		normal_fetch = 0, 
		texture_fetch[MAX_TEXTURE_UNITS] = {0, };
	GLuint (*index_fetch)(GLvoid **indices);
	GLvoid *index_pointer = (GLvoid *)indices;

#ifdef MGL_COMPILED_VERTEX_ARRAYS
	if (mode == GL_TRIANGLES && context->LockedArrays)
	{
		/* Try an optimized version */
		GLDrawElementsTriangles(context, count, type, indices);
		return;
	}
#endif

	/* Determine the index type */
	switch (type)
	{
		case GL_UNSIGNED_BYTE:
			index_fetch = FetchElementByte;
			break;
		case GL_UNSIGNED_SHORT:
			index_fetch = FetchElementShort;
			break;
		case GL_UNSIGNED_INT:
			index_fetch = FetchElementInt;
			break;
		default:
			GLFlagError(context, 1, GL_INVALID_ENUM);
	}
	
	/* Some special handling for certain primitive types */	
	switch (mode)
	{
		case GL_TRIANGLES:
			pack_size = pack_size - (pack_size % 3);
			break;
		case GL_QUADS:
			pack_size = pack_size - (pack_size % 4);
			break;
		case GL_TRIANGLE_FAN:
		case MGL_FLATFAN:
			verts_to_copy = 1;
			break;
		case GL_TRIANGLE_STRIP:
		case GL_QUAD_STRIP:
			verts_to_copy = 2;
			break;
	}
	
	full_packs = count / pack_size;
	part_verts = count % pack_size;
	
	/* Setup the array and function pointers(if active) */
	if (context->vertex_array.ClientState & GLCS_COLOR)
	{
		color_fetch = context->vertex_array.ColorArray.fetchIdx;
		GLFlagError(context, color_fetch == 0, GL_INVALID_ENUM);
	}
	else
		color_fetch = (ArrayFetcherIdxFn)CurrentColorFetch;
		
	if (context->vertex_array.ClientState & GLCS_NORMAL)
	{
		normal_fetch = context->vertex_array.NormalArray.fetchIdx;
		GLFlagError(context, normal_fetch == 0, GL_INVALID_ENUM);		
	}
	else
		normal_fetch = (ArrayFetcherIdxFn)CurrentNormalFetch;	
		
	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->vertex_array.ClientState & client_texture_state[i] 
		&& context->enable.Texture2D[i])
		{
			texture_fetch[i] = context->vertex_array.TexCoordArray[i].fetchIdx;
			GLFlagError(context, texture_fetch[i] == 0, GL_INVALID_ENUM);
		}
		else
			texture_fetch[i] = (ArrayFetcherIdxFn)CurrentTextureFetch;
	}
	
	if (context->vertex_array.ClientState & GLCS_VERTEX)
	{
		vertex_fetch = context->vertex_array.VertexArray.fetchIdx;
	}

	GLFlagError(context, vertex_fetch == 0, GL_INVALID_ENUM);

	/* Start drawing */		
	while (full_packs--)
	{
		if (GL_FALSE == ArrayBegin(context, mode))
			return;		
		
		/* On subsequent passes, strips and fans need "key" vertices copied */
		if (pass != 0)
		{
			if (verts_to_copy == 1)
			{
				context->VertexBuffer[0] = t1;
				context->VertexBufferPointer = 1;
			}
			else if (verts_to_copy == 2)
			{
				context->VertexBuffer[0] = t1;
				context->VertexBuffer[1] = t2;
				context->VertexBufferPointer = 2;
			}
		}
		
		for (c = 0; c < pack_size; c++)
		{
			GLuint idx = index_fetch(&index_pointer);
			
			color_fetch(context, &context->vertex_array.ColorArray, idx, 0);
			
			normal_fetch(context, &context->vertex_array.NormalArray, idx, 0);
			
			for (i = 0; i <= context->texture.MaxTextureUnit; i++)
			{
				if (context->enable.Texture2D[i])
					texture_fetch[i](context, 
						&context->vertex_array.TexCoordArray[i], idx, i);
			}
			
			vertex_fetch(context, &context->vertex_array.VertexArray, idx, 0);
			
			context->VertexBufferPointer ++;
		}
		
		pass++;
		
		if (verts_to_copy == 1)
		{
			t1 = context->VertexBuffer[0];
		}
		else if (verts_to_copy == 2)
		{
			t1 = context->VertexBuffer[context->VertexBufferPointer-2];
			t2 = context->VertexBuffer[context->VertexBufferPointer-1];
		}
		
		ArrayEnd(context);
	}
	
	/* Draw the rest which didn't fit the full packages */
	if (part_verts)
	{
		if (GL_FALSE == ArrayBegin(context, mode))
			return;		
	
		for (c = 0; c < part_verts; c++)
		{
			GLuint idx = index_fetch(&index_pointer);
			
			color_fetch(context, &context->vertex_array.ColorArray, idx, 0);
			
			normal_fetch(context, &context->vertex_array.NormalArray, idx, 0);
			
			for (i = 0; i <= context->texture.MaxTextureUnit; i++)
			{
				if (context->enable.Texture2D[i])
					texture_fetch[i](context, 
						&context->vertex_array.TexCoordArray[i], idx, i);
			}
			
			vertex_fetch(context, &context->vertex_array.VertexArray, idx, 0);
			
			context->VertexBufferPointer ++;
		}	
		
		ArrayEnd(context);
	}
}

/* The following code is experimental and incomplete. It tries to implement
 * GL_compiled_vertex_arrays. Use with caution.
 */

#ifdef MGL_COMPILED_VERTEX_ARRAYS
void v_MaybeTransform(GLcontext context, GLuint *or_codes, GLuint *and_codes)
{
	int i, tmu;
	
	*or_codes = 0;
	*and_codes = 0xffffffff;
		
	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);
#ifndef QUAKE3_HACK
			float a41 = a(41);
			float a42 = a(42);
			float a44 = a(44);
#endif

			for (i = 0; i < context->VertexBufferPointer; i++)
			{					
				MGLVertex *vx = &context->VertexBuffer[i];

				if (vx->tex_frame_code != context->TexFrameCode)
				{
					float u = vx->uvw[tmu].u;
					float v = vx->uvw[tmu].v;
					float w __attribute__((unused)) = vx->uvw[tmu].w;

#ifdef QUAKE3_HACK
					vx->uvw[tmu].u = a11*u + a12*v + a14;
					vx->uvw[tmu].v = a21*u + a22*v + a24;
					vx->rhw_valid[tmu] = GL_FALSE;
					vx->align[0] = 1;
#else
 					if (vx->rhw_valid == GL_FALSE)
					{
						vx->uvw[tmu].u = a11*u + a12*v + a14;
						vx->uvw[tmu].v = a21*u + a22*v + a24;
						vx->rhw_valid[tmu] = GL_FALSE;
						vx->align[0] = 1;
					}
					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;
					}
#endif
					vx->tex_frame_code = context->TexFrameCode;
				}
				
				#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];
			
	    	if (v->frame_code != context->FrameCode)
			{
				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;
			
				hc_CodePoint(context, v);
	
				v->frame_code = context->FrameCode;
				v->align[0] = 2;
			}

			if (v->align[0])
				v_ToScreen(context, i);
				
			*or_codes |= v->outcode;
			*and_codes &= v->outcode;

	    }

	    #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];

	    	if (v->frame_code != context->FrameCode)
			{
			    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;

				hc_CodePoint(context, v);
					    
	   			v->frame_code = context->FrameCode;
				v->align[0] = 2;
			}

			if (v->align[0])
				v_ToScreen(context, i);

			*or_codes |= v->outcode;
			*and_codes &= v->outcode;
	    }
	#undef a
	}
}

#define NEED_SPECIAL_FILL (context->polygon.PolygonModeFront != GL_FILL \
						|| context->polygon.PolygonModeBack  != GL_FILL)

extern GLint v_GetPolyFillMode(GLcontext context, MGLPolygon *poly);
extern void d_ClipAndDrawPolyWithFill(GLcontext context, MGLPolygon *poly, 
	uint32 or_code, GLuint fillMode);

void GLDrawElementsTriangles(GLcontext context, GLsizei count, GLenum type, const GLvoid *indices)
{
	GLuint (*index_fetch)(GLvoid **indices);
	GLvoid *index_pointer = (GLvoid *)indices;
	uint32 w3dType;

	ArrayFetcherFn
		color_fetch = 0, 
		vertex_fetch = 0, 
		normal_fetch = 0, 
		texture_fetch[MAX_TEXTURE_UNITS] = {0, };
	GLubyte 
		*color = 0,
		*vertex = 0,
		*normal = 0,
		*texture[MAX_TEXTURE_UNITS] = {0, };
	GLuint drawBase;
	GLuint drawCount;
	GLuint indexScale;
	int i, tmu;
	GLuint and_code, or_code;
	GLbitfield clientState[10] = { GLCS_TEXTURE0, GLCS_TEXTURE1, GLCS_TEXTURE2, GLCS_TEXTURE3, 0,};
	GLfloat offset;
	GLuint first = context->LockArraysFirst;
	
	/* Determine the index type */
	switch (type)
	{
		case GL_UNSIGNED_BYTE:
			index_fetch = FetchElementByte;
			w3dType = W3D_INDEX_UBYTE;
			indexScale = 1;
			break;
		case GL_UNSIGNED_SHORT:
			index_fetch = FetchElementShort;
			w3dType = W3D_INDEX_UWORD;
			indexScale = 2;
			break;
		case GL_UNSIGNED_INT:
			index_fetch = FetchElementInt;
			w3dType = W3D_INDEX_ULONG;
			indexScale = 4;
			break;
		default:		
			GLFlagError(context, 1, GL_INVALID_ENUM);
	}
	
	checkNeedEye(context);

	/* Setup the array and function pointers (if active) */
	if (context->vertex_array.ClientState & GLCS_COLOR)
	{
		color_fetch = context->vertex_array.ColorArray.fetch;
		color = ArrayAddress(&context->vertex_array.ColorArray, first);
		GLFlagError(context, color_fetch == 0, GL_INVALID_ENUM);
	}
	else
		color_fetch = CurrentColorFetch;
		
	if (context->vertex_array.ClientState & GLCS_NORMAL)
	{
		normal_fetch = context->vertex_array.NormalArray.fetch;
		normal = ArrayAddress(&context->vertex_array.NormalArray, first);
		GLFlagError(context, normal_fetch == 0, GL_INVALID_ENUM);		
	}
	else
		normal_fetch = CurrentNormalFetch;	
		
	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->vertex_array.ClientState & client_texture_state[i]
		&& context->enable.Texture2D[i])
		{
			texture_fetch[i] = context->vertex_array.TexCoordArray[i].fetch;
			texture[i] = ArrayAddress(
						&context->vertex_array.TexCoordArray[i], first);
			GLFlagError(context, texture_fetch[i] == 0, GL_INVALID_ENUM);
		}
		else
			texture_fetch[i] = CurrentTextureFetch;
	}
	
	if (context->vertex_array.ClientState & GLCS_VERTEX)
	{
		vertex_fetch = context->vertex_array.VertexArray.fetch;
		vertex = ArrayAddress(&context->vertex_array.VertexArray, first);
	}

	GLFlagError(context, vertex_fetch == 0, GL_INVALID_ENUM);

	and_code = 0xff;
	or_code = 0;

	/* First of all, fetch all vertices */
	context->VertexBufferPointer = 0;
	for (i = 0; i < context->LockArraysCount; i++)
	{
		if (!(context->LockedArrays & GLCS_NORMAL) ||
			context->VertexBuffer[i].frame_code != context->FrameCode)
		{
			normal_fetch(context, &context->vertex_array.NormalArray, normal, 0);
		}

		if (!(context->LockedArrays & GLCS_COLOR) ||
			context->VertexBuffer[i].frame_code != context->FrameCode)
		{
			color_fetch(context, &context->vertex_array.ColorArray, color, 0);
		}
		
		for (tmu = 0; tmu <= context->texture.MaxTextureUnit; tmu++)
		{
			if (!context->enable.Texture2D[tmu])
				continue;
				
			if (!(context->LockedArrays & clientState[tmu]) ||
				context->VertexBuffer[i].frame_code != context->FrameCode)
			{
				texture_fetch[tmu](context, 
						&context->vertex_array.TexCoordArray[tmu], 
						texture[tmu], tmu);
			}
		}

		if (!(context->LockedArrays & GLCS_VERTEX) ||
			context->VertexBuffer[i].frame_code != context->FrameCode)
		{
			vertex_fetch(context, &context->vertex_array.VertexArray, vertex, 0);
		}
		
		color += context->vertex_array.ColorArray.stride;
		normal += context->vertex_array.NormalArray.stride;
		vertex += context->vertex_array.VertexArray.stride;

		for (tmu = 0; tmu <= context->texture.MaxTextureUnit; tmu++)
		{
			if (context->enable.Texture2D[tmu])
				texture[tmu] += context->vertex_array.TexCoordArray[tmu].stride;
		}
		
		context->VertexBufferPointer ++;
	}
	
	/* Transform what we need */
	v_MaybeTransform(context, &or_code, &and_code);

	/* If the and_codes are not 0, no triangle is on screen. Likewise, if the
	 * or code is all 0, all triangles are completely on-screen
	 */
	 
#if 0
	if (and_code)
		return;
#endif

#if 1
	if (or_code == 0 && !context->enable.PolygonOffsetFill
		&& !NEED_SPECIAL_FILL) 
	{
		MaybeLock(context);
		 
		if (context->enable.CullFace)
			IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, W3D_ENABLE);

		IWarp3D->W3D_DrawElements(context->w3dContext,
				 W3D_PRIMITIVE_TRIANGLES, w3dType, count, index_pointer);


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

		MaybeUnlock(context);
		
		return;
	}
#endif
	MaybeLock(context);
	
	/* If */
	drawBase = 0;
	drawCount = 0;
		
	/* Go through the array and draw */
	for (i = 0; i < count; i+=3)	
	{
		void *idxbase = index_pointer;
		GLfloat z1 = 0.0, 
				z2 = 0.0, 
				z3 = 0.0;
		
		GLuint idx1 = index_fetch(&index_pointer) - first;
		GLuint idx2 = index_fetch(&index_pointer) - first;
		GLuint idx3 = index_fetch(&index_pointer) - first;
		
		or_code = context->VertexBuffer[idx1].outcode
				| context->VertexBuffer[idx2].outcode
				| context->VertexBuffer[idx3].outcode;

		and_code = context->VertexBuffer[idx1].outcode
				 & context->VertexBuffer[idx2].outcode
				 & context->VertexBuffer[idx3].outcode;

		if (and_code)
			continue;
			
		if ((hc_DecideFrontface(context, &(context->VertexBuffer[idx1]),
			&(context->VertexBuffer[idx2]),
			&(context->VertexBuffer[idx3]),or_code) == GL_FALSE))
		{
			continue;
		}

		if (context->enable.PolygonOffsetFill)
		{
			offset = v_PolygonOffset(context, idx1, idx2, idx3);

			z1 = context->VertexBuffer[idx1].z;
			z2 = context->VertexBuffer[idx2].z;
			z3 = context->VertexBuffer[idx3].z;

			context->VertexBuffer[idx1].z = min (1.0, context->VertexBuffer[idx1].z + offset);
			context->VertexBuffer[idx2].z = min (1.0, context->VertexBuffer[idx2].z + offset);
			context->VertexBuffer[idx3].z = min (1.0, context->VertexBuffer[idx3].z + offset);
		}
		else
			offset = 0.0;
		
		if (or_code == 0 && !NEED_SPECIAL_FILL)
		{
			IWarp3D->W3D_DrawElements(context->w3dContext,
					 W3D_PRIMITIVE_TRIANGLES, w3dType, 3, idxbase);			
		}
		else
		{
			static MGLPolygon poly;
			poly.numverts = 3;
			poly.verts[0] = idx1;
			poly.verts[1] = idx2;
			poly.verts[2] = idx3;
			poly.zoffset = 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);
		}

		if (context->enable.PolygonOffsetFill)
		{
			context->VertexBuffer[idx1].z = z1;
			context->VertexBuffer[idx2].z = z2;
			context->VertexBuffer[idx3].z = z3;
		}
	}
	
	MaybeUnlock(context);
}

#endif

