// gl_arrays.cpp: vertex array management 
// by Knightmare

#include "gl_local.h"

unsigned	indexArray[MAX_INDICES];
float		texCoordArray[MAX_TMUS][MAX_VERTICES][2];
float		vertexArray[MAX_VERTICES][3];
float		colorArray[MAX_VERTICES][4];

unsigned	rb_vertex, rb_index;

//====================================================================

/*
===============
R_InitVertexArrays
===============
*/
void R_InitVertexArrays (void)
{
	if ( gl_config.havedrawelements || gl_config.havedrawrangeelements )
	{
		qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
		qglEnableClientState(GL_VERTEX_ARRAY);
		qglEnableClientState(GL_COLOR_ARRAY);

		qglTexCoordPointer(2, GL_FLOAT, sizeof(texCoordArray[0][0]), texCoordArray[0][0]);
		qglVertexPointer(3, GL_FLOAT, sizeof(vertexArray[0]), vertexArray[0]);
		qglColorPointer(4, GL_FLOAT, sizeof(colorArray[0]), colorArray[0]);
	}

	rb_vertex = rb_index = 0;
}


/*
===============
R_CheckArrayOverflow
===============
*/
qboolean R_CheckArrayOverflow (int numVerts, int numIndex)
{
	if (rb_vertex == 0 || rb_index == 0)	return false;

	if (numVerts> MAX_VERTICES)
		ri.Sys_Error (ERR_DROP, "R_CheckArrayOverflow: %i > MAX_VERTICES", numVerts);
	if (numIndex > MAX_INDICES)
		ri.Sys_Error (ERR_DROP, "R_CheckArrayOverflow: %i > MAX_INDICES", numIndex);

	if ( ((rb_vertex + numVerts) <= MAX_VERTICES) && ((rb_index + numIndex) <= MAX_INDICES) )
		return false;

//	ri.Con_Printf( PRINT_DEVELOPER, "R_CheckArrayOverflow: purging %i verts and %i indices to prevent overflow from %i, %i more\n", rb_vertex, rb_index, numVerts, numIndex);

	return true;
}


/*
===============
R_DrawElementImmediate
===============
*/
void APIENTRY R_DrawElementImmediate (GLint index)
{
	qglColor4fv(colorArray[index]);

	// check if multitexture active
	if (gl_state.TMU_active[1])
	{
		qglMultiTexCoord2fARB(GL_TEXTURE0_ARB+0, texCoordArray[0][index][0], texCoordArray[0][index][1]);
		qglMultiTexCoord2fARB(GL_TEXTURE0_ARB+1, texCoordArray[1][index][0], texCoordArray[1][index][1]);
		if (gl_state.TMU_active[2]) {
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB+2, texCoordArray[2][index][0], texCoordArray[2][index][1]);
			if (gl_state.TMU_active[3]) 
				qglMultiTexCoord2fARB(GL_TEXTURE0_ARB+3, texCoordArray[3][index][0], texCoordArray[3][index][1]);
		}
	}
	else
		qglTexCoord2f(texCoordArray[0][index][0], texCoordArray[0][index][1]);

	qglVertex3fv(vertexArray[index]);
}


/*
===============
R_DrawElementsWrapper
===============
*/
void R_DrawElementsWrapper (int numIndex, unsigned *indices, void (APIENTRY *elementFunc)(GLint index))
{
	int			i, prev[3];
	qboolean	evenTris = false;

	// check if any verts to render
	if (numIndex < 3)	return;

	qglBegin(GL_TRIANGLE_STRIP);

	// first triangle
	elementFunc (indices[0]);
	elementFunc (indices[1]);
	elementFunc (indices[2]);
	prev[0] = indices[0];
	prev[1] = indices[1];
	prev[2] = indices[2];

	for (i=3; i<numIndex; i+=3)
	{
		if (evenTris)
		{
			if ( (prev[0] == indices[i+0]) && (prev[2] == indices[i+1]) )	// continuing a strip
				elementFunc (indices[i+2]);
			else	// starting a new strip 
			{
				qglEnd();
				qglBegin(GL_TRIANGLE_STRIP);

				elementFunc (indices[i+0]);
				elementFunc (indices[i+1]);
				elementFunc (indices[i+2]);
			}
			evenTris = false;
		}
		else	// odd triangle
		{
			if ( (prev[1] == indices[i+1]) && (prev[2] == indices[i+0]) ) {	// continuing a strip
				elementFunc (indices[i+2]);
				evenTris = true;
			}
			else	// starting a new strip 
			{
				qglEnd();
				qglBegin(GL_TRIANGLE_STRIP);

				elementFunc (indices[i+0]);
				elementFunc (indices[i+1]);
				elementFunc (indices[i+2]);
				evenTris = false;
			}
		}
		prev[0] = indices[i+0];
		prev[1] = indices[i+1];
		prev[2] = indices[i+2];
	}
	qglEnd();
}


/*
===============
R_DrawArrays
===============
*/
void R_DrawArrays (void)
{
	if (rb_vertex == 0 || rb_index == 0)	// nothing to render
		return;
	if ( (!gl_config.havedrawelements && !gl_config.havedrawrangeelements)
		|| ( gl_state.TMU_active[1] && !qglClientActiveTextureARB) || gl_render_immediate->value != 0 )
		R_DrawElementsWrapper (rb_index, indexArray, R_DrawElementImmediate);
	else if ( gl_config.havedrawrangeelements )
		qglDrawRangeElementsEXT(GL_TRIANGLES, 0, rb_vertex, rb_index, GL_UNSIGNED_INT, indexArray);
	else
		qglDrawElements(GL_TRIANGLES, rb_index, GL_UNSIGNED_INT, indexArray);
}


/*
===============
R_DrawRangeArrays
===============
*/
void R_DrawRangeArrays (unsigned start, unsigned end)
{
	unsigned count;

	if (rb_vertex == 0 || end == 0 || end <= start)	// nothing to render
		return;

	count = end - start + 1;
	if ( (!gl_config.havedrawelements && !gl_config.havedrawrangeelements)
		|| ( gl_state.TMU_active[1] && !qglClientActiveTextureARB) || gl_render_immediate->value != 0 )
		R_DrawElementsWrapper (count, &indexArray[start], R_DrawElementImmediate);
	else if ( gl_config.havedrawrangeelements )
		qglDrawRangeElementsEXT(GL_TRIANGLES, 0, rb_vertex, count, GL_UNSIGNED_INT, &indexArray[start]);
	else
		qglDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, &indexArray[start]);
}


/*
===============
R_DrawPrimitiveArrays
===============
*/
void R_DrawPrimitiveArrays (GLenum mode)
{
	if (rb_vertex == 0 || rb_index == 0)	// nothing to render
		return;
	if ( gl_config.havedrawrangeelements )
		qglDrawRangeElementsEXT(mode, 0, rb_vertex, rb_index, GL_UNSIGNED_INT, indexArray);
	else
		qglDrawElements(mode, rb_index, GL_UNSIGNED_INT, indexArray);
}


/*
===============
R_DrawMeshTris
===============
*/
void R_DrawMeshTris (void)
{
	int		i, numTMUs = 0;

	if (!gl_showtris->value)	return;

	if (gl_showtris->value != 2)
		qglDisable (GL_DEPTH_TEST);

	qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
	for (i=0; i<MAX_TMUS; i++)
		if (gl_state.TMU_active[i]) {
			numTMUs++;
			GL_EnableTexture (i, false);
		}
	qglDisableClientState(GL_COLOR_ARRAY);
	qglColor4f(1.0f, 1.0f, 1.0f, 1.0f);

	R_DrawArrays ();

	qglEnableClientState(GL_COLOR_ARRAY);
	for (i=0; i<numTMUs; i++)
		GL_EnableTexture (i, true);
	qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);

	if ( gl_showtris->value != 2 && (gl_state.glStateFlags & GLSTATE_DEPTH_TEST) )
		qglEnable (GL_DEPTH_TEST);
}


/*
===============
R_RenderMeshGeneric
===============
*/
void R_RenderMeshGeneric (qboolean drawTris)
{
	R_DrawArrays ();
	if (drawTris)
		 R_DrawMeshTris ();
	rb_vertex = rb_index = 0;
}
