/* -*- mode: C; tab-width:8; c-basic-offset:2 -*- */

/*
 * Mesa 3-D graphics library
 * Version:  3.3
 *
 * Copyright (C) 1999  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *
 * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
 * terms stated above.
 *
 * Thank you for your contribution, David!
 *
 * Please make note of the above copyright/license statement.  If you
 * contributed code or bug fixes to this code under the previous (GNU
 * Library) license and object to the new license, your code will be
 * removed at your request.  Please see the Mesa docs/COPYRIGHT file
 * for more information.
 *
 * Additional Mesa/3Dfx driver developers:
 *   Daryll Strauss <daryll@precisioninsight.com>
 *   Keith Whitwell <keith@precisioninsight.com>
 *
 * See fxapi.h for more revision/author details.
 */


/* fxrender.c - 3Dfx VooDoo RenderVB driver function support */


#ifdef HAVE_CONFIG_H
#include "conf.h"
#endif

#if defined(FX)

#include "fxdrv.h"
#include "vbcull.h"

/*
 * Render a line segment from VB[v1] to VB[v2] when either one or both
 * endpoints must be clipped.
 */
//#if !defined(__MWERKS__)
//INLINE                                /* function is accessed in fxcva.c! */
//#endif
void fxRenderClippedLine( struct vertex_buffer *VB,
                                 GLuint v1, GLuint v2 )
{
  fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;
  GLubyte mask = VB->ClipMask[v1] | VB->ClipMask[v2];

  if (!mask || (VB->ctx->line_clip_tab[VB->ClipPtr->size])(VB, &v1, &v2, mask))
    FX_grDrawLine((GrVertex *)gWin[v1].f,(GrVertex *)gWin[v2].f);
}




/* This is legal for Quads as well as triangles, hence the 'n' parameter.
 */
INLINE void fxRenderClippedTriangle( struct vertex_buffer *VB,
                                         GLuint n, GLuint vlist[] )
{
  GLubyte mask = 0;
  GLuint i;

  for (i = 0 ; i < n ; i++)
    mask |= VB->ClipMask[vlist[i]];

  if (mask & CLIP_USER_BIT) {
    GLubyte *userclipmask = VB->UserClipMask;
    if (userclipmask[vlist[0]] & userclipmask[vlist[1]] & userclipmask[vlist[2]])
      return;
  }

  n = (VB->ctx->poly_clip_tab[VB->ClipPtr->size])( VB, n, vlist, mask );
  if (n >= 3) {
    fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;
    GrVertex *i0 = (GrVertex *)gWin[vlist[0]].f;
    GrVertex *i1 = (GrVertex *)gWin[vlist[1]].f;
    GrVertex *i2 = (GrVertex *)gWin[vlist[2]].f;
    GLuint i;

    for (i=2;i<n;i++, i1 = i2, i2 = (GrVertex *)gWin[vlist[i]].f) {
      FX_grDrawTriangle(i0,i1,i2);
    }
  }
}





static INLINE void fxSafeClippedLine( struct vertex_buffer *VB,
                                      GLuint v1, GLuint v2 )
{
  GLubyte mask = VB->ClipMask[v1] | VB->ClipMask[v2];

  if (!mask) {
    fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;
    FX_grDrawLine((GrVertex *)gWin[v1].f,(GrVertex *)gWin[v2].f);
  } else {
    fxMesaContext fxMesa = (fxMesaContext)VB->ctx->DriverCtx;
    fxLineClipTab[fxMesa->setupindex & 0x7]( VB, v1, v2, mask );
  }
}


static INLINE void fxSafeClippedTriangle( struct vertex_buffer *VB,
                                          fxVertex *gWin,
                                          tfxTriClipFunc cliptri,
                                          GLuint v2, GLuint v1, GLuint v )
{
  GLubyte *clipmask = VB->ClipMask;
  GLubyte mask = clipmask[v2] | clipmask[v1] | clipmask[v];

  if (!mask) {
    FX_grDrawTriangle((GrVertex *)gWin[v2].f,
                      (GrVertex *)gWin[v1].f,
                      (GrVertex *)gWin[v].f);
    return;
  }

  if (!(clipmask[v2] & clipmask[v1] & clipmask[v] & CLIP_ALL_BITS))
  {
    GLuint vl[3];
    GLuint imask = mask;

    if (imask & CLIP_USER_BIT) {
      GLubyte *userclipmask = VB->UserClipMask;
      if (userclipmask[v2] & userclipmask[v1] & userclipmask[v])
        return;
      imask |= (userclipmask[v2] | userclipmask[v1] | userclipmask[v]) << 8;
    }

    ASSIGN_3V(vl, v2, v1, v );
    cliptri( VB, vl, imask );
  }
}


static INLINE void fxSafeClippedTriangle2( struct vertex_buffer *VB,
                                           fxVertex *gWin,
                                           tfxTriViewClipFunc cliptri,
                                           GLuint v2, GLuint v1, GLuint v )
{
  GLubyte *clipmask = VB->ClipMask;
  GLubyte mask = clipmask[v2] | clipmask[v1] | clipmask[v];

  if (!mask) {
    FX_grDrawTriangle((GrVertex *)gWin[v2].f,(GrVertex *)gWin[v1].f,
                      (GrVertex *)gWin[v].f);
  } else if (!(clipmask[v2] & clipmask[v1] & clipmask[v])) {
    GLuint vl[3];
    ASSIGN_3V(vl, v2, v1, v );
    cliptri( VB, vl, mask );
  }
}


static INLINE void fxSafeClippedTriangle3( struct vertex_buffer *VB,
                                           fxVertex *gWin,
                                           tfxTriClipFunc cliptri,
                                           GLuint v2, GLuint v1, GLuint v )
{
  GLubyte *clipmask = VB->ClipMask;
  GLubyte mask = clipmask[v2] | clipmask[v1] | clipmask[v];
  GLuint imask = mask;

  if (imask & CLIP_USER_BIT) {
    GLubyte *userclipmask = VB->UserClipMask;
    if (userclipmask[v2] & userclipmask[v1] & userclipmask[v])
      return;
    imask |= (userclipmask[v2] | userclipmask[v1] | userclipmask[v]) << 8;
  }

  {
    GLuint vl[3];
    ASSIGN_3V(vl, v2, v1, v );
    cliptri( VB, vl, imask );
  }
}





/************************************************************************/
/************************ RenderVB functions ****************************/
/************************************************************************/


/* Render front-facing, non-clipped primitives.
 */

#define RENDER_POINTS( start, count )                           \
   (void) gWin;                                                 \
   (void) VB;                                                   \
   (VB->ctx->Driver.PointsFunc)( VB->ctx, start, count-1 )

#define RENDER_LINE( i1, i )                    \
  do {                                          \
     RVB_COLOR(i);                              \
     FX_grDrawLine((GrVertex *)gWin[i1].f,      \
                (GrVertex *)gWin[i].f);         \
  } while (0)

#define RENDER_TRI( i2, i1, i, pv, parity )     \
  do {                                          \
    RVB_COLOR(pv);                              \
    if (parity) {                               \
      FX_grDrawTriangle((GrVertex *)gWin[i1].f, \
                      (GrVertex *)gWin[i2].f,   \
                      (GrVertex *)gWin[i].f);   \
    } else {                                    \
      FX_grDrawTriangle((GrVertex *)gWin[i2].f, \
                      (GrVertex *)gWin[i1].f,   \
                      (GrVertex *)gWin[i].f);   \
    }                                           \
  } while (0)

#define RENDER_QUAD( i3, i2, i1, i, pv )        \
  do {                                          \
    RVB_COLOR(pv);                              \
    FX_grDrawTriangle((GrVertex *)gWin[i3].f,   \
                    (GrVertex *)gWin[i2].f,     \
                    (GrVertex *)gWin[i].f);     \
    FX_grDrawTriangle((GrVertex *)gWin[i2].f,   \
                    (GrVertex *)gWin[i1].f,     \
                    (GrVertex *)gWin[i].f);     \
  } while (0)



#define LOCAL_VARS                                              \
   fxMesaContext fxMesa=(fxMesaContext)VB->ctx->DriverCtx;      \
   fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;                  \
   (void) fxMesa;

#define INIT(x)

#define TAG(x) x##_fx_flat_raw
#undef  RVB_COLOR
#define RVB_COLOR(pv)  FX_VB_COLOR(fxMesa, VB->ColorPtr->data[pv])
#define PRESERVE_VB_DEFS

#include "render_tmp.h"

#define TAG(x) x##_fx_smooth_raw
#undef  RVB_COLOR
#define RVB_COLOR(x)

#include "render_tmp.h"



/* Render with clipped and/or culled primitives with cullmask information.
 */
#define RENDER_POINTS( start, count )                           \
   (void) gWin;                                                 \
   (void) cullmask;                                             \
   (VB->ctx->Driver.PointsFunc)( VB->ctx, start, count-1 )


#define RENDER_LINE( i1, i )                                            \
  do {                                                                  \
    const GLubyte flags = cullmask[i];                                  \
                                                                        \
    if (!(flags & PRIM_NOT_CULLED))                                     \
      continue;                                                         \
                                                                        \
    RVB_COLOR(i);                                                       \
    if (flags & PRIM_ANY_CLIP)                                          \
      fxRenderClippedLine( VB, i1, i );                                 \
    else                                                                \
      FX_grDrawLine( (GrVertex *)gWin[i1].f, (GrVertex *)gWin[i].f );   \
 } while (0)


#define RENDER_TRI( i2, i1, i, pv, parity)              \
  do {                                                  \
    const GLubyte flags = cullmask[i];                  \
    GLuint e2,e1;                                       \
                                                        \
    if (!(flags & PRIM_NOT_CULLED))                     \
      continue;                                         \
                                                        \
     e2=i2, e1=i1;                                      \
     if (parity) { e2=i1; e1=i2; }                      \
                                                        \
     RVB_COLOR(pv);                                     \
     if (flags & PRIM_ANY_CLIP) {                       \
       fxSafeClippedTriangle3(VB,gWin,cliptri,e2,e1,i); \
     } else {                                           \
       FX_grDrawTriangle((GrVertex *)gWin[e2].f,        \
                    (GrVertex *)gWin[e1].f,             \
                    (GrVertex *)gWin[i].f);             \
     }                                                  \
  } while (0)


#define RENDER_QUAD(i3, i2, i1, i, pv)                  \
  do {                                                  \
    const GLubyte flags = cullmask[i];                  \
                                                        \
    if (!(flags & PRIM_NOT_CULLED))                     \
      continue;                                         \
                                                        \
    RVB_COLOR(pv);                                      \
    if (flags&PRIM_ANY_CLIP) {                          \
      fxSafeClippedTriangle3(VB,gWin,cliptri,i3,i2,i);  \
      fxSafeClippedTriangle3(VB,gWin,cliptri,i2,i1,i);  \
    } else {                                            \
      FX_grDrawTriangle((GrVertex *)gWin[i3].f,         \
                     (GrVertex *)gWin[i2].f,            \
                     (GrVertex *)gWin[i].f);            \
      FX_grDrawTriangle((GrVertex *)gWin[i2].f,         \
                     (GrVertex *)gWin[i1].f,            \
                     (GrVertex *)gWin[i].f);            \
    }                                                   \
  } while (0)





#define LOCAL_VARS                                              \
   fxMesaContext fxMesa=(fxMesaContext)VB->ctx->DriverCtx;      \
   fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;                  \
   const GLubyte *cullmask = VB->CullMask;                      \
   tfxTriClipFunc cliptri = fxMesa->clip_tri_stride;




#define INIT(x)     (void) cliptri; (void) fxMesa;

#define TAG(x) x##_fx_smooth_culled
#undef  RVB_COLOR
#define RVB_COLOR(x)
#define PRESERVE_VB_DEFS
#include "render_tmp.h"

#define TAG(x) x##_fx_flat_culled
#undef  RVB_COLOR
#define RVB_COLOR(pv)  FX_VB_COLOR(fxMesa, VB->ColorPtr->data[pv])

#include "render_tmp.h"




/* Direct, with the possibility of clipping.
 */
#define RENDER_POINTS( start, count )           \
  do {                                          \
    fxVertex *gWin = FX_DRIVER_DATA(VB)->verts; \
    GLubyte *clipmask = VB->ClipMask;           \
    GLuint i;                                   \
    for (i = start ; i <= count ; i++)          \
      if (clipmask[i] == 0) {                   \
        RVB_COLOR(i);                           \
        FX_grDrawPoint( (GrVertex *)gWin[i].f );\
      }                                         \
  } while (0)

#define RENDER_LINE( i1, i )                    \
  do {                                          \
    RVB_COLOR(i);                               \
    fxSafeClippedLine( VB, i1, i );             \
  } while (0)

#define RENDER_TRI( i2, i1, i, pv, parity)              \
  do {                                                  \
    GLuint e2=i2, e1=i1;                                        \
    if (parity) { GLuint t=e2; e2=e1; e1=t; }           \
    RVB_COLOR(pv);                                      \
    fxSafeClippedTriangle(VB,gWin,cliptri,e2,e1,i);     \
  } while (0)

#define RENDER_QUAD( i3, i2, i1, i, pv)                 \
  do {                                                  \
    RVB_COLOR(pv);                                      \
    fxSafeClippedTriangle(VB,gWin,cliptri,i3,i2,i);     \
    fxSafeClippedTriangle(VB,gWin,cliptri,i2,i1,i);     \
  } while (0)

#define LOCAL_VARS                                              \
   fxMesaContext fxMesa=(fxMesaContext)VB->ctx->DriverCtx;      \
   fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;                  \
   tfxTriClipFunc cliptri = fxMesa->clip_tri_stride;

#define INIT(x)  (void) cliptri; (void) gWin;

#define TAG(x) x##_fx_smooth_clipped
#undef  RVB_COLOR
#define RVB_COLOR(x)
#define PRESERVE_VB_DEFS
#include "render_tmp.h"


#define TAG(x) x##_fx_flat_clipped
#undef  RVB_COLOR
#define RVB_COLOR(pv)  FX_VB_COLOR(fxMesa, VB->ColorPtr->data[pv])
#include "render_tmp.h"






/* Indirect, with the possibility of clipping.
 */
#define RENDER_POINTS( start, count )                   \
  do {                                                  \
    fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;         \
    GLuint e;                                           \
    GLubyte *clipmask = VB->ClipMask;                   \
    for(e=start;e<=count;e++)                           \
      if(clipmask[elt[e]]==0) {                         \
        FX_grDrawPoint((GrVertex *)gWin[elt[e]].f);     \
      }                                                 \
  } while (0)

#define RENDER_LINE( i1, i )                    \
  do {                                          \
    GLuint e1 = elt[i1], e = elt[i];            \
    RVB_COLOR(e);                               \
    fxSafeClippedLine( VB, e1, e );             \
  } while (0)

#define RENDER_TRI( i2, i1, i, pv, parity)              \
  do {                                                  \
    GLuint e2 = elt[i2], e1 = elt[i1], e = elt[i];      \
    if (parity) { GLuint t=e2; e2=e1; e1=t; }           \
    fxSafeClippedTriangle(VB,gWin,cliptri,e2,e1,e);     \
  } while (0)

#define RENDER_QUAD( i3, i2, i1, i, pv)                         \
  do {                                                          \
    GLuint e3 = elt[i3], e2 = elt[i2], e1 = elt[i1], e = elt[i];\
    fxSafeClippedTriangle(VB,gWin,cliptri,e3,e2,e);             \
    fxSafeClippedTriangle(VB,gWin,cliptri,e2,e1,e);             \
  } while (0)

#define LOCAL_VARS const GLuint *elt = VB->EltPtr->data;        \
   fxMesaContext fxMesa = (fxMesaContext)VB->ctx->DriverCtx;    \
   fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;                  \
   tfxTriClipFunc cliptri = fxMesa->clip_tri_stride;

#define INIT(x)  (void) cliptri; (void) gWin;

#define TAG(x) x##_fx_smooth_indirect_clipped
#undef  RVB_COLOR
#define RVB_COLOR(x)
#include "render_tmp.h"


/* Indirect, clipped, but no user clip.
 */
#define RENDER_POINTS( start, count )                   \
  do {                                                  \
    fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;         \
    GLuint e;                                           \
    GLubyte *clipmask = VB->ClipMask;                   \
    for(e=start;e<=count;e++)                           \
      if(clipmask[elt[e]]==0) {                         \
        FX_grDrawPoint((GrVertex *)gWin[elt[e]].f);     \
      }                                                 \
  } while (0)

#define RENDER_LINE( i1, i )                    \
  do {                                          \
    GLuint e1 = elt[i1], e = elt[i];            \
    RVB_COLOR(e);                               \
    fxSafeClippedLine( VB, e1, e );             \
  } while (0)

#define RENDER_TRI( i2, i1, i, pv, parity)              \
  do {                                                  \
    GLuint e2 = elt[i2], e1 = elt[i1], e = elt[i];      \
    if (parity) { GLuint t=e2; e2=e1; e1=t; }           \
    fxSafeClippedTriangle2(VB,gWin,cliptri,e2,e1,e);    \
  } while (0)

#define RENDER_QUAD( i3, i2, i1, i, pv)                         \
  do {                                                          \
    GLuint e3 = elt[i3], e2 = elt[i2], e1 = elt[i1], e = elt[i];\
    fxSafeClippedTriangle2(VB,gWin,cliptri,e3,e2,e);            \
    fxSafeClippedTriangle2(VB,gWin,cliptri,e2,e1,e);            \
  } while (0)

#define LOCAL_VARS const GLuint *elt = VB->EltPtr->data;        \
   fxMesaContext fxMesa = (fxMesaContext)VB->ctx->DriverCtx;    \
   fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;                  \
   tfxTriViewClipFunc cliptri = fxMesa->view_clip_tri;

#define INIT(x)  (void) cliptri; (void) gWin;

#define TAG(x) x##_fx_smooth_indirect_view_clipped
#undef  RVB_COLOR
#define RVB_COLOR(x)
#include "render_tmp.h"







/* Indirect, and no clipping required.
 */
#define RENDER_POINTS( start, count )                   \
  do {                                                  \
    GLuint e;                                           \
    for(e=start;e<=count;e++) {                         \
      FX_grDrawPoint((GrVertex *)gWin[elt[e]].f);       \
    }                                                   \
  } while (0)

#define RENDER_LINE( i1, i )                                            \
  do {                                                                  \
    GLuint e1 = elt[i1], e = elt[i];                                    \
    FX_grDrawLine((GrVertex *)gWin[e1].f, (GrVertex *)gWin[e].f);       \
  } while (0)


#define RENDER_TRI( i2, i1, i, pv, parity)              \
  do {                                                  \
    GLuint e2 = elt[i2], e1 = elt[i1], e = elt[i];      \
    if (parity) {GLuint tmp = e2; e2 = e1; e1 = tmp;}   \
    FX_grDrawTriangle((GrVertex *)gWin[e2].f,           \
                 (GrVertex *)gWin[e1].f,                \
                 (GrVertex *)gWin[e].f);                \
  } while (0)


#define RENDER_QUAD( i3, i2, i1, i, pv)                         \
  do {                                                          \
    GLuint e3 = elt[i3], e2 = elt[i2], e1 = elt[i1], e = elt[i];\
    FX_grDrawTriangle((GrVertex *)gWin[e3].f,                   \
                 (GrVertex *)gWin[e2].f,                        \
                 (GrVertex *)gWin[e].f);                        \
    FX_grDrawTriangle((GrVertex *)gWin[e2].f,                   \
                 (GrVertex *)gWin[e1].f,                        \
                 (GrVertex *)gWin[e].f);                        \
  } while (0)

#define LOCAL_VARS                              \
   fxVertex *gWin = FX_DRIVER_DATA(VB)->verts;  \
   const GLuint *elt = VB->EltPtr->data;

#define INIT(x)

#define TAG(x) x##_fx_smooth_indirect
#undef  RVB_COLOR
#define RVB_COLOR(x)
#include "render_tmp.h"





/* Direct in this context means that triangles, lines, points can be
 * rendered simply by calling grDrawTriangle, etc., without any
 * additional setup (such as calling grConstantColor).  We also use a
 * 'safe' set of clipping routines which don't require write-access to
 * the arrays in the vertex buffer, and don't care about array
 * stride.
 *
 * Thus there is no call to gl_import_arrays() in this function.
 *
 * This safe clipping should be generalized to call driver->trianglefunc
 * under the appropriate conditions.
 *
 * We don't handle texcoord-4 in the safe clip routines - maybe we should.
 *
 */
void fxDDRenderElementsDirect( struct vertex_buffer *VB )
{
  GLcontext *ctx = VB->ctx;
  struct vertex_buffer *saved_vb = ctx->VB;
  GLenum prim = ctx->CVA.elt_mode;
  GLuint nr = VB->EltPtr->count;
  render_func func = render_tab_fx_smooth_indirect[prim];
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  GLuint p = 0;

  if (!nr)
    return;

  if (fxMesa->new_state)
    fxSetupFXUnits(ctx);

  if (!nr) return;

  if (VB->ClipOrMask) {
    func = render_tab_fx_smooth_indirect_view_clipped[prim];
    if (VB->ClipOrMask & CLIP_USER_BIT)
      func = render_tab_fx_smooth_indirect_clipped[prim];
  }

  ctx->VB = VB;                 /* kludge */

  do {
    func( VB, 0, nr, 0 );
  } while (ctx->Driver.MultipassFunc &&
           ctx->Driver.MultipassFunc( VB, ++p ));


  ctx->VB = saved_vb;
}


void fxDDRenderVBIndirectDirect( struct vertex_buffer *VB )
{
  GLcontext *ctx = VB->ctx;
  struct vertex_buffer *cvaVB = ctx->CVA.VB;
  struct vertex_buffer *saved_vb = ctx->VB;
  GLuint i, next, count = VB->Count;
  render_func *tab = render_tab_fx_smooth_indirect;
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  GLuint p = 0;

  if (cvaVB->ClipOrMask)
    tab = render_tab_fx_smooth_indirect_clipped;

  if (!VB->CullDone)
    gl_fast_copy_vb( VB );

  if (fxMesa->new_state)
    fxSetupFXUnits( ctx );

  ctx->VB = cvaVB;
  cvaVB->EltPtr = VB->EltPtr;

  do {
    GLuint parity = VB->Parity;

    for (i = VB->CopyStart ; i < count ; parity = 0, i = next)
    {
      GLuint prim = VB->Primitive[i];
      next = VB->NextPrimitive[i];
      tab[prim]( cvaVB, i, next, parity );
    }
    /* loop never taken */
  } while (ctx->Driver.MultipassFunc &&
           ctx->Driver.MultipassFunc( cvaVB, ++p ));

  cvaVB->EltPtr = 0;
  ctx->VB = saved_vb;
}


static render_func *fxDDRenderVBSmooth_tables[3] = {
  render_tab_fx_smooth_clipped,
  render_tab_fx_smooth_culled,
  render_tab_fx_smooth_raw
};

static render_func *fxDDRenderVBFlat_tables[3] = {
  render_tab_fx_flat_clipped,
  render_tab_fx_flat_culled,
  render_tab_fx_flat_raw
};


static render_func *null_tables[3] = {
  0,
  0,
  0
};

#if defined(FX_GLIDE3)
#include "fxstripdet.c"
#endif

void fxDDRenderInit( GLcontext *ctx )
{
   render_init_fx_smooth_indirect_view_clipped();
   render_init_fx_smooth_indirect_clipped();
   render_init_fx_smooth_indirect();
   render_init_fx_smooth_raw();
   render_init_fx_smooth_culled();
   render_init_fx_smooth_clipped();
   render_init_fx_flat_raw();
   render_init_fx_flat_culled();
   render_init_fx_flat_clipped();
#if defined(FX_GLIDE3)
   fxDDRenderInitGlide3(ctx);
#endif
}


/* Now used to set an internal var in fxMesa - we hook out at the
 * level of gl_render_vb() instead.
 */
render_func **fxDDChooseRenderVBTables(GLcontext *ctx)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

  if (ctx->IndirectTriangles & DD_SW_SETUP)
    return null_tables;

  switch (fxMesa->render_index) {
  case FX_FLAT:
    return fxDDRenderVBFlat_tables;
  case 0:
    return fxDDRenderVBSmooth_tables;
  default:
    return null_tables;
  }
}


void fxDDDoRenderVB( struct vertex_buffer *VB )
{
   GLcontext *ctx = VB->ctx;
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
   GLuint i, next, prim;
   GLuint parity = VB->Parity;
   render_func *tab;
   GLuint count = VB->Count;
   GLint p = 0;

   if (fxMesa->new_state)
     fxSetupFXUnits(ctx);

   if (VB->Indirect) {
      return;
   } else if (VB->CullMode & CLIP_MASK_ACTIVE) {
      tab = fxMesa->RenderVBClippedTab;
   } else {
      tab = fxMesa->RenderVBRawTab;
   }

   if (!VB->CullDone)
      gl_fast_copy_vb( VB );

   do
   {
      for ( i= VB->CopyStart ; i < count ; parity = 0, i = next )
      {
         prim = VB->Primitive[i];
         next = VB->NextPrimitive[i];
         tab[prim]( VB, i, next, parity );
      }

   } while (ctx->Driver.MultipassFunc &&
            ctx->Driver.MultipassFunc( VB, ++p ));
}


#else


/*
 * Need this to provide at least one external definition.
 */

int gl_fx_dummy_function_render(void)
{
  return 0;
}

#endif  /* FX */
