//////////////////////////////////////////////////////////////////////////////
//
//  RenderFuncs.h
//
//////////////////////////////////////////////////////////////////////////////

#ifndef _RENDERFUNCS_H_
#define _RENDERFUNCS_H_

#include "Params.h"

enum Method {
  Immediate = 0,
  DisplayList,
  ArrayElement,
  DrawArrays,
  DrawElements,
  InterleavedArrayElement,
  InterleavedDrawArrays,
  InterleavedDrawElements,
  NumMethods
};

char*  methodName[NumMethods] = {
  "Immediate",
  "DisplayList",
  "ArrayElement",
  "DrawArrays",
  "DrawElements",
  "InterleavedArrayElement",
  "InterleavedDrawArrays",
  "InterleavedDrawElements"
};

struct ClearColor {
  GLfloat  r, g, b, a;
} clear[NumMethods] = {
  { 0, 0, 0, 1 },
  { 1, 0, 0, 1 },
  { 0, 1, 0, 1 },
  { 0, 0, 1, 1 },
  { 1, 0, 1, 1 },
  { 0, 1, 1, 1 }
};

static GLuint  triList;

//----------------------------------------------------------------------------
//
//  Immediate Mode Routines
//

void
immediate( void )
{
  int  i;

  GLfloat*  v = vertexData;
  GLfloat*  c = colorData;
  GLfloat*  n = normalData;
  GLfloat*  t = texCoordData;

  glBegin( primType );
  switch( renderMode ) {
    case Vertex:
      for ( i = 0; i < numVertices; ++i ) {
	glVertex3fv( v ); v += 3;
      }
    break;

    case Vertex|Color:
      for ( i = 0; i < numVertices; ++i ) {
	glColor3fv( c );  c += 3;
	glVertex3fv( v ); v += 3;
      }
    break;

    case Vertex|Normal:
      for ( i = 0; i < numVertices; ++i ) {
	glNormal3fv( n ); n += 3;
	glVertex3fv( v ); v += 3;
      }
      break;

    case Vertex|TexCoord:
      for ( i = 0; i < numVertices; ++i ) {
	glTexCoord2fv( t ); t += 2;
	glVertex3fv( v );   v += 3;
      }
      break;

    case Vertex|Color|Normal:
      for ( i = 0; i < numVertices; ++i ) {
	glColor4fv( c );  c += 4;	
	glNormal3fv( n ); n += 3;
	glVertex3fv( v ); v += 3;
      }
      break;

    case Vertex|Color|TexCoord:
      for ( i = 0; i < numVertices; ++i ) {
	glTexCoord2fv( t ); t += 2;
	glColor3fv( c );    c += 3;
	glVertex3fv( v );   v += 3;
      }
      break;

    case Vertex|Color|Normal|TexCoord:
      for ( i = 0; i < numVertices; ++i ) {
	glTexCoord2fv( t ); t += 2;
	glColor4fv( c );    c += 4;
	glNormal3fv( n );   n += 3;
	glVertex3fv( v );   v += 3;
      }
      break;
  }

  glEnd();
}


//----------------------------------------------------------------------------
//
//  Display list rendering
//

void
dlistSetup( void )
{
  triList = glGenLists( 1 );
  glNewList( triList, GL_COMPILE );
  immediate();
  glEndList();
}

void
dlist( void )
{
  glCallList( triList );
}

void
dlistFinish( void )
{
  glDeleteLists( triList, 1 );
}


//----------------------------------------------------------------------------
//
//  Vertex array rendering routines.
//
//    Since the setup and finish of the glArrayElement() and glDrawArrays()
//    tests are identical, we reuse the functions.  Additionally, the
//    rendering methods for interleaved arrays are identical to using
//    individual vertex arrays, so we'll reuse those rendering functions
//    as well.
//

void
pointerSetup( void )
{
  if ( renderMode & Color )
    glEnableClientState( GL_COLOR_ARRAY );

  if ( renderMode & Normal )
    glEnableClientState( GL_NORMAL_ARRAY );

  if ( renderMode & TexCoord )
    glEnableClientState( GL_TEXTURE_COORD_ARRAY );

  glEnableClientState( GL_VERTEX_ARRAY );
}

void
interleavedSetup( void )
{
  GLenum  format[] = {
    GL_V3F,
    GL_C3F_V3F,
    GL_N3F_V3F,
    GL_C4F_N3F_V3F,
    GL_T2F_V3F,
    GL_T2F_C3F_V3F,
    GL_T2F_N3F_V3F,
    GL_T2F_C4F_N3F_V3F
  };

  glInterleavedArrays( format[renderMode], 0, interleavedData );
}

void
arrayElem( void )
{
  int  i = 0;

  glBegin( primType );
  for ( i = 0; i < numVertices; ++i )
    glArrayElement( i );
  glEnd();
}

void
drawArrays( void )
{
  glDrawArrays( primType, 0, numVertices );
}

void
drawElements( void )
{
  glDrawElements( primType, numVertices, GL_UNSIGNED_INT, indices );
}

void
pointerFinish( void )
{
  if ( renderMode & Color )
    glDisableClientState( GL_COLOR_ARRAY );

  if ( renderMode & Normal )
    glDisableClientState( GL_NORMAL_ARRAY );

  if ( renderMode & TexCoord )
    glDisableClientState( GL_TEXTURE_COORD_ARRAY );

  glDisableClientState( GL_VERTEX_ARRAY );
}


//----------------------------------------------------------------------------
//
//  Function arrays
//


void (*setup[])( void ) = {
  NULL,
  dlistSetup,
  pointerSetup,
  pointerSetup,
  pointerSetup,
  interleavedSetup,
  interleavedSetup,
  interleavedSetup,
};

void (*render[])( void ) = {
  immediate,
  dlist,
  arrayElem,
  drawArrays,
  drawElements,
  arrayElem,
  drawArrays,
  drawElements
};

void (*finish[])( void ) = {
  NULL,
  dlistFinish,
  pointerFinish,
  pointerFinish,
  pointerFinish,
  NULL,
  NULL,
  NULL
};

#endif /* ! _RENDERFUNCS_H_ */
