/* polygons.c */

/*
 * Mesa 3-D graphics library
 * Version:  1.2
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
$Id: polygons.c,v 1.34 1995/11/30 00:21:55 brianp Exp $

$Log: polygons.c,v $
 * Revision 1.34  1995/11/30  00:21:55  brianp
 * restored old gl_polygon_edge function
 *
 * Revision 1.33  1995/11/14  21:49:40  brianp
 * optimized polygon rendering setup
 *
 * Revision 1.32  1995/11/04  20:09:07  brianp
 * optimized gl_polygon_edge()
 *
 * Revision 1.31  1995/10/29  19:14:44  brianp
 * added glPolygonOffsetEXT display list support
 *
 * Revision 1.30  1995/10/27  20:30:29  brianp
 * added glPolygonOffsetEXT()
 *
 * Revision 1.29  1995/10/24  20:52:15  brianp
 * renamed COMPUTE_PLANE_Z as gl_compute_z
 * renamed polygon_edge as gl_polygon_edge
 * use new color interpolation based on fixed point arithmetic
 * removed dead code
 *
 * Revision 1.28  1995/10/22  21:20:50  brianp
 * changed 0.5 to 0.5F in COMPUTE_PLANE_Z calls
 *
 * Revision 1.27  1995/10/22  20:29:37  brianp
 * removed dead code
 *
 * Revision 1.26  1995/10/19  15:48:28  brianp
 * added gamma support
 * changed DD.color arguments to GLubytes
 *
 * Revision 1.25  1995/10/17  21:40:04  brianp
 * added fast_flat_rgba_z_polygon() function
 * removed fast_ci/rgb_polygon() functions
 *
 * Revision 1.24  1995/09/20  18:20:39  brianp
 * prototype device driver changes described
 *
 * Revision 1.23  1995/09/13  14:51:05  brianp
 * use CC.NewState convention
 * use DEFARRAY/UNDEFARRAY macros for Mac
 * replaced VB.Vs and VB.Vt with VB.TexCoord
 *
 * Revision 1.22  1995/07/28  21:34:16  brianp
 * support polygons draw in viewport larger than window
 *
 * Revision 1.21  1995/07/15  14:04:50  brianp
 * fixed texture coord interpolation bug
 *
 * Revision 1.20  1995/07/11  17:47:45  brianp
 * round window coords to nearest int, not truncate, in fast_ci|rgba_polygon
 *
 * Revision 1.19  1995/06/20  16:32:40  brianp
 * new depth buffer value computations
 * introduced new triangle rasterizer code, experimental, not used yet
 *
 * Revision 1.18  1995/06/12  15:43:35  brianp
 * changed color arrays to GLubyte
 * new interpolation functions
 *
 * Revision 1.17  1995/06/05  20:27:53  brianp
 * added CC.Polygon.Unfilled stuff
 *
 * Revision 1.16  1995/06/02  13:59:46  brianp
 * added fast_smooth_rgba_z_polygon() function
 *
 * Revision 1.15  1995/05/31  19:34:12  brianp
 * replaced MAX_VERTICES with VB_MAX
 *
 * Revision 1.14  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.13  1995/05/12  17:01:05  brianp
 * changed CC.Mode!=0 to INSIDE_BEGIN_END
 *
 * Revision 1.12  1995/04/18  15:48:23  brianp
 * fixed assignment of NULL to function pointers to prevent warnings on Suns
 *
 * Revision 1.11  1995/04/12  15:36:15  brianp
 * updated to use DD.draw_* function pointers
 *
 * Revision 1.10  1995/03/27  20:31:53  brianp
 * new Texture.Enabled scheme
 *
 * Revision 1.9  1995/03/24  19:28:13  brianp
 * introduced VB
 *
 * Revision 1.8  1995/03/07  14:20:59  brianp
 * updated for new XSetForeground/GC scheme
 *
 * Revision 1.7  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.6  1995/03/02  19:18:54  brianp
 * new RasterMask logic
 * fixed Vcolor/Vbcolor bug in textured_polygon()
 *
 * Revision 1.5  1995/02/27  22:49:01  brianp
 * modified for PB
 *
 * Revision 1.4  1995/02/27  15:08:17  brianp
 * added Vcolor/Vindex scheme
 *
 * Revision 1.3  1995/02/26  22:58:57  brianp
 * made COMPUTE_PLANE_Z a function w/ FP overflow checking
 *
 * Revision 1.2  1995/02/25  22:07:43  brianp
 * more debugging code
 *
 * Revision 1.1  1995/02/24  14:26:49  brianp
 * Initial revision
 *
 */


#include <string.h>
#include "context.h"
#include "dd.h"
#include "feedback.h"
#include "gamma.h"
#include "interp.h"
#include "lines.h"
#include "list.h"
#include "macros.h"
#include "points.h"
#include "span.h"
#include "vb.h"

/*#include "triangle.h"*/


void glCullFace( GLenum mode )
{
   if (CC.CompileFlag) {
      gl_save_cullface( mode );
   }
   if (CC.ExecuteFlag) {
      if (mode!=GL_FRONT && mode!=GL_BACK) {
	 gl_error( GL_INVALID_ENUM, "glCullFace" );
	 return;
      }
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glCullFace" );
	 return;
      }
      CC.Polygon.CullFaceMode = mode;
      CC.NewState = GL_TRUE;
   }
}



void glFrontFace( GLenum mode )
{
   if (CC.CompileFlag) {
      gl_save_frontface( mode );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glFrontFace" );
	 return;
      }
      if (mode!=GL_CW && mode!=GL_CCW) {
	 gl_error( GL_INVALID_ENUM, "glFrontFace" );
	 return;
      }
      CC.Polygon.FrontFace = mode;
   }  
}



void glPolygonMode( GLenum face, GLenum mode )
{
   if (CC.CompileFlag) {
      gl_save_polygonmode( face, mode );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glPolygonMode" );
	 return;
      }
      if (face!=GL_FRONT && face!=GL_BACK && face!=GL_FRONT_AND_BACK) {
	 gl_error( GL_INVALID_ENUM, "glPolygonMode(face)" );
	 return;
      }
      else if (mode!=GL_POINT && mode!=GL_LINE && mode!=GL_FILL) {
	 gl_error( GL_INVALID_ENUM, "glPolygonMode(mode)" );
	 return;
      }

      if (face==GL_FRONT || face==GL_FRONT_AND_BACK) {
	 CC.Polygon.FrontMode = mode;
      }
      if (face==GL_BACK || face==GL_FRONT_AND_BACK) {
	 CC.Polygon.BackMode = mode;
      }

      /* Compute a handy "shortcut" value: */
      if (CC.Polygon.FrontMode!=GL_FILL || CC.Polygon.BackMode!=GL_FILL) {
	 CC.Polygon.Unfilled = GL_TRUE;
      }
      else {
	 CC.Polygon.Unfilled = GL_FALSE;
      }

      CC.NewState = GL_TRUE;
   }
}



void glPolygonStipple( const GLubyte *mask )
{
   if (CC.CompileFlag) {
      /*gl_save_polygon_stipple( mask );*/
   }
   if (CC.ExecuteFlag) {
      /* TODO:  bit twiddling, unpacking */
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glPolygonStipple" );
	 return;
      }
      MEMCPY( CC.PolygonStipple, mask, 32*sizeof(GLuint) );
   }
}



void glGetPolygonStipple( GLubyte *mask )
{
   /* TODO */
}



void glPolygonOffsetEXT( GLfloat factor, GLfloat bias )
{
   if (CC.CompileFlag) {
      gl_save_polygonoffset( factor, bias );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
         gl_error( GL_INVALID_OPERATION, "glPolygonOffsetEXT" );
         return;
      }
      CC.Polygon.OffsetFactor = factor;
      CC.Polygon.OffsetBias = bias;
   }
}



/**********************************************************************/
/*****                    Rasterization                           *****/
/**********************************************************************/


/*
 * Summary of polygon drawing functions:
 *   feedback_polygon - when rendering mode is GL_FEEDBACK
 *   select_polygon - when rendering mode is GL_SELECT
 *   flat_ci_polygon - flat-shaded, any raster ops, RGBA polygon
 *   smooth_ci_polygon - smooth-shaded, any raster ops, CI polygon
 *   flat_rgba_polygon - flat-shaded, any raster ops, RGBA polygon
 *   smooth_rgba_polygon - smooth-shaded, any raster ops, RGBA polygon
 *   textured_polygon - smooth-shaded, textured RGBA polygon
 */


/*
 * All polygon drawing functions have the same arguments:
 * n      - number of vertices
 * vlist  - array of indexes into the vertex list
 * pv     - provoking vertex: which vertex color to use for flat shading.
 */



/*
 * Evaluate the current polygon's plane equation at (x,y) to get Z.
 */
GLint gl_compute_z( GLfloat x, GLfloat y )
{
   GLfloat fz;

   fz = (CC.PlaneD - CC.PlaneA*x - CC.PlaneB*y) / CC.PlaneC;

   if (fz<0.0F) {
      return 0;
   }
   else if (fz>1.0F) {
      return MAX_DEPTH;
   }

   return (GLint) (fz * DEPTH_SCALE);
}


/* This macro can't be used for now.  Sometimes the evaluation of the
 * plane equation at (x,y) results in a z which is too large or small
 * to store in an integer, causing a FP exception.  We check for this
 * case in the above function.

#define gl_compute_z( X, Y )  \
	((CC.PlaneD - CC.PlaneA*(X) - CC.PlaneB*(Y)) / CC.PlaneC)
 */




/*
 * Put polygon in feedback buffer.
 */
static void feedback_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLuint i;

   APPEND_TOKEN( (GLfloat) GL_POLYGON_TOKEN );
   APPEND_TOKEN( (GLfloat) n );

   for (i=0;i<n;i++) {
      GLfloat x, y, z, w;
      GLfloat tc[4];
      GLuint j = vlist[i];

      x = VB.Win[j][0];
      y = VB.Win[j][1];
      z = VB.Win[j][2];
      w = VB.Clip[j][3];

      tc[0] = VB.TexCoord[j][0];
      tc[1] = VB.TexCoord[j][1];
      tc[2] = 0.0F;          /* TODO: R, Q components */
      tc[3] = 1.0F;

      gl_feedback_vertex( x, y, z, w, VB.Color[j], VB.Index[j], tc );
   }
}



/*
 * Put polygon in selection buffer.
 */
static void select_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLuint i;

   CC.HitFlag = GL_TRUE;
   for (i=0;i<n;i++) {
      GLuint j = vlist[i];
      GLfloat wz = VB.Win[j][2];
      if (wz < CC.HitMinZ) {
	 CC.HitMinZ = wz;
      }
      if (wz > CC.HitMaxZ) {
	 CC.HitMaxZ = wz;
      }
   }
}



/* Max number of pixels along a polygon's edge */
#if MAX_WIDTH > MAX_HEIGHT
#  define EDGEMAX MAX_WIDTH
#else
#  define EDGEMAX MAX_HEIGHT
#endif


/*
 * Left and right boundary values for polygon scan conversion
 */
static GLfloat flx[MAX_HEIGHT], frx[MAX_HEIGHT];/* X bounds */
static GLint li[MAX_HEIGHT], ri[MAX_HEIGHT];	/* Color Index */
static GLint lr[MAX_HEIGHT], rr[MAX_HEIGHT];	/* Red */
static GLint lg[MAX_HEIGHT], rg[MAX_HEIGHT];	/* Green */
static GLint lb[MAX_HEIGHT], rb[MAX_HEIGHT];	/* Blue */
static GLint la[MAX_HEIGHT], ra[MAX_HEIGHT];	/* Alpha */
static GLfloat ls[MAX_HEIGHT], rs[MAX_HEIGHT];	/* S */
static GLfloat lt[MAX_HEIGHT], rt[MAX_HEIGHT];	/* T */



#ifdef LEAVEOUT
/* THIS IS EXPERIMENTAL, NOT USED. */
void gl_setup_edge( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
                    GLfloat *x0, GLfloat *dx,
                    GLint *ymin, GLint *ymax, GLint *dy )
{
   GLfloat fy, slope;

   if (y1==y2) {
      /* skip horizontal edges */
      *ymin = *ymax = 0;
      return;
   }

   *dx = slope = (x2-x1) / (y2-y1);

   if (y1<y2) {
      *ymin = (int) (y1 + 0.5F);
      *ymax = (int) (y2 + 0.5F) - 0;  /* ADD one? */
      fy = *ymin + 0.5F - y1;
      *x0 = x1 + fy * slope;
      *dy = 1;
   }
   else {
      *ymin = (int) (y2 + 0.5F);
      *ymax = (int) (y1 + 0.5F) - 0;  /* ADD one? */
      fy = *ymax + 0.5F - y1;
      *x0 = x1 + fy * slope;
      *dx = -slope;
      *dy = 1;
   }
}
#endif


/*
 * Compute the location of the pixels along the edge of a polygon.
 * Input:  x1,y1, x2,y2 - endpoints of the polygon edge
 * Output:  x, y - array of edge coords
 * Return:  number of elements in x[], and y[]
 */
#ifdef LEAVEOUT
/* This version should be faster but unfortunately introduces a sampling bug */
GLuint gl_polygon_edge( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
                        GLfloat x[], GLint y[])
{
   GLint ymin, ymax, iy;
   GLfloat slope, fx, fy;
   GLuint n;

   if (y1==y2) {
      /* skip horizontal edges */
      return 0;
   }

   slope = (x2-x1) / (y2-y1);
   n = 0;
   if (y1<y2) {
      ymin = (int) (y1 + 0.5F);
      ymax = (int) (y2 + 0.5F) - 1;
      fy = ymin + 0.5F - y1;
      fx = x1 + fy * slope;
      for (iy=ymin; iy<=ymax; iy++, fx+=slope, n++) {
	 x[n] = fx;
	 y[n] = iy;
      }
   }
   else {
      ymin = (int) (y2 + 0.5F);
      ymax = (int) (y1 + 0.5F) - 1;
      fy = ymax + 0.5F - y1;
      fx = x1 + fy * slope;
      for (iy=ymax; iy>=ymin; iy--, fx-=slope, n++) {
	 x[n] = fx;
	 y[n] = iy;
      }
   }
   return n;
}
#else
/* this versions works but isn't the most efficient */
GLuint gl_polygon_edge( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
                            GLfloat x[], GLint y[])
{
   GLint ymin, ymax, iy;
   GLfloat slope;
   GLuint i;

   if (y1==y2) {
      /* skip horizontal edges */
      return 0;
   }

   slope = (x2-x1) / (y2-y1);
   i = 0;

   if (y1<y2) {
      ymin = (int) (y1 + 0.5F);
      ymax = (int) (y2 + 0.5F) - 1;
      for (iy=ymin; iy<=ymax; iy++) {
         x[i] = x1 + ((iy+0.5F) - y1) * slope;
         y[i] = iy;
         i++;
      }
   }
   else {
      ymin = (int) (y2 + 0.5F);
      ymax = (int) (y1 + 0.5F) - 1;
      for (iy=ymax; iy>=ymin; iy--) {
         x[i] = x1 + ((iy+0.5F) - y1) * slope;
         y[i] = iy;
         i++;
      }
   }
   return i;
}

#endif



static void flat_ci_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;
   GLuint index;

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   /* process edges to compute span bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      GLfloat ex[EDGEMAX];
      GLint ey[EDGEMAX];

      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* update bounds */
      for (j=0;j<len;j++) {
	 GLfloat x = ex[j];
	 GLint y = ey[j];

	 if (y>=0 && y<MAX_HEIGHT) {
	    if (x<flx[y]) {
	       flx[y] = x;
	    }
	    if (x>frx[y]) {
	       frx[y] = x;
	    }
	 }
      }
   }

   if (!VB.MonoColor) {
      /* set the color index */
      index = (GLuint) (GLint) VB.Index[pv];
      (*DD.index)( index );
   }

   /* process spans */
   /* TODO: don't always have to compute pixel depths! */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint z0, z1;
	 GLint zspan[MAX_WIDTH];

	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 gl_interpolate_i( len, z0, z1, zspan );

	 gl_write_monoindex_span( len, xmin, y, zspan, index, GL_POLYGON );
      }
   }
}



static void smooth_ci_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   /* process edges to compute bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      GLfloat ex[EDGEMAX];
      GLint ey[EDGEMAX];
      GLuint ei[EDGEMAX];

      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* interpolate index along edge */
      gl_interpolate_i( len,
		        (GLint) VB.Index[j0], (GLint) VB.Index[j1],
		        (GLint *) ei );

      /* update bounds */
      for (j=0;j<len;j++) {
	 GLfloat x = ex[j];
	 GLint y = ey[j];

	 if (y>=0 && y<MAX_DEPTH) {
	    if (x < flx[y]) {
	       flx[y] = x;
	       li[y] = ei[j];
	    }
	    if (x > frx[y]) {
	       frx[y] = x;
	       ri[y] = ei[j];
	    }
	 }
      }
   }

   /* process spans */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint z0, z1;
	 GLint zspan[MAX_WIDTH];
	 GLuint index[MAX_WIDTH];

	 /* interpolate z and index along span */
	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 gl_interpolate_i( len, z0, z1, zspan );
	 gl_interpolate_i( len, li[y], ri[y], (GLint *) index );

	 gl_write_index_span( len, xmin, y, zspan, index, GL_POLYGON );
      }
   }
}



static void flat_rgba_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   /* process edges to compute span bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      GLfloat ex[EDGEMAX];
      GLint ey[EDGEMAX];

      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* update bounds */
      for (j=0;j<len;j++) {
	 GLfloat x = ex[j];
	 GLint y = ey[j];

	 if (y>=0 && y<MAX_HEIGHT) {
	    if (x<flx[y]) {
	       flx[y] = x;
	    }
	    if (x>frx[y]) {
	       frx[y] = x;
	    }
	 }
      }
   }

   if (!VB.MonoColor) {
      /* set the color */
      GLubyte r = (GLint) (VB.Color[pv][0] * CC.RedScale);
      GLubyte g = (GLint) (VB.Color[pv][1] * CC.GreenScale);
      GLubyte b = (GLint) (VB.Color[pv][2] * CC.BlueScale);
      GLubyte a = (GLint) (VB.Color[pv][3] * CC.AlphaScale);
      if (CC.RasterMask & GAMMA_BIT) {
         gl_apply_gamma( 1, &r, &g, &b );
      }
      (*DD.color)( r, g, b,a );
   }

   /* process spans */
   /* TODO: don't always have to compute pixel depths! */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint z0, z1;
	 GLint zspan[MAX_WIDTH];

	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 gl_interpolate_i( len, z0, z1, zspan );

	 gl_write_monocolor_span( len, xmin, y, zspan, VB.Color[pv],
				  GL_POLYGON );
      }
   }
}




static void smooth_rgba_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   /* process edges to compute bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      GLfloat ex[EDGEMAX];
      GLint ey[EDGEMAX];
      GLint r0, g0, b0, a0, r1, g1, b1, a1, dr, dg, db, da;

      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* setup for edge color interpolation */
      r0 = (GLint) (VB.Color[j0][0] * CC.RedScale) << 8;
      r1 = (GLint) (VB.Color[j1][0] * CC.RedScale) << 8;
      g0 = (GLint) (VB.Color[j0][1] * CC.GreenScale) << 8;
      g1 = (GLint) (VB.Color[j1][1] * CC.GreenScale) << 8;
      b0 = (GLint) (VB.Color[j0][2] * CC.BlueScale) << 8;
      b1 = (GLint) (VB.Color[j1][2] * CC.BlueScale) << 8;
      a0 = (GLint) (VB.Color[j0][3] * CC.AlphaScale) << 8;
      a1 = (GLint) (VB.Color[j1][3] * CC.AlphaScale) << 8;
      if (len>1) {
         GLint n = len-1;
         dr = (r1-r0) / n;
         dg = (g1-g0) / n;
         db = (b1-b0) / n;
         da = (a1-a0) / n;
      }
      else {
         dr = dg = db = da = 0;
      }

      /* update span bounds */
      for (j=0;j<len;j++) {
	 GLfloat x = ex[j];
	 GLint y = ey[j];

	 if (y>=0 && y<MAX_HEIGHT) {
	    if (x < flx[y]) {
	       flx[y] = x;
	       lr[y] = r0 >> 8;
	       lg[y] = g0 >> 8;
	       lb[y] = b0 >> 8;
	       la[y] = a0 >> 8;
	    }
	    if (x > frx[y]) {
	       frx[y] = x;
	       rr[y] = r0 >> 8;
	       rg[y] = g0 >> 8;
	       rb[y] = b0 >> 8;
	       ra[y] = a0 >> 8;
	    }
	 }
         r0 += dr;   g0 += dg;   b0 += db;   a0 += da;
      }
   }

   /* process spans */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint z0, z1;
	 GLint zspan[MAX_WIDTH];
	 GLubyte red[MAX_WIDTH];
	 GLubyte green[MAX_WIDTH];
	 GLubyte blue[MAX_WIDTH];
	 GLubyte alpha[MAX_WIDTH];

	 /* interpolate z and colors */
	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 GL_INTERPOLATE_I( len, z0, z1, zspan );
         GL_INTERPOLATE_4UB( len,
                             lr[y], rr[y], red,
                             lg[y], rg[y], green,
                             lb[y], rb[y], blue,
                             la[y], ra[y], alpha );

	 gl_write_color_span( len, xmin, y, zspan,
			      red, green, blue, alpha, GL_POLYGON );
      }
   }
}



/*
 * This is a very special case function:  RGBA mode, flat shaded,
 * depth buffered (GL_LESS) polygon.
 */
static void fast_flat_rgba_z_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   if (!VB.MonoColor) {
      /* set the color */
      GLubyte r = (GLint) (VB.Color[pv][0] * CC.RedScale);
      GLubyte g = (GLint) (VB.Color[pv][1] * CC.GreenScale);
      GLubyte b = (GLint) (VB.Color[pv][2] * CC.BlueScale);
      GLubyte a = (GLint) (VB.Color[pv][3] * CC.AlphaScale);
      if (CC.RasterMask & GAMMA_BIT) {
         gl_apply_gamma( 1, &r, &g, &b );
      }
      (*DD.color)( r, g, b,a );
   }

   /* process edges to compute bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      GLfloat ex[EDGEMAX];
      GLint ey[EDGEMAX];

      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* update span bounds */
      for (j=0;j<len;j++) {
	 GLfloat x = ex[j];
	 GLint y = ey[j];
	 if (y>=0 && y<MAX_HEIGHT) {
	    if (x < flx[y]) {
	       flx[y] = x;
	    }
	    if (x > frx[y]) {
	       frx[y] = x;
	    }
	 }
      }
   }

   /* process spans */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint zspan[MAX_WIDTH];
	 GLubyte mask[MAX_WIDTH];
	 GLint z0, z1, *zptr;
         GLuint passed = 0;

	 /* interpolate z */
	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 GL_INTERPOLATE_I( len, z0, z1, zspan );

	 /* do depth test */
	 zptr = CC.DepthBuffer + y * CC.BufferWidth + xmin;
	 for (i=0;i<len;i++) {
	    if (zspan[i]<zptr[i]) {
	       zptr[i] = zspan[i];
	       mask[i] = 1;
               passed++;
	    }
	    else {
	       mask[i] = 0;
	    }
	 }
	 /* write pixels */
         if (passed>0) {
            (*DD.write_monocolor_span)( len, xmin, y, mask );
         }
      }
   }
}



/*
 * This is a very special case function:  RGBA mode, smooth shaded,
 * depth buffered (GL_LESS) polygon.
 */
static void fast_smooth_rgba_z_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   /* process edges to compute bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      GLfloat ex[EDGEMAX];
      GLint ey[EDGEMAX];
      GLint r0, g0, b0, a0, r1, g1, b1, a1, dr, dg, db, da;

      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* interpolate colors along edge */
      r0 = (GLint) (VB.Color[j0][0] * CC.RedScale) << 8;
      r1 = (GLint) (VB.Color[j1][0] * CC.RedScale) << 8;
      g0 = (GLint) (VB.Color[j0][1] * CC.GreenScale) << 8;
      g1 = (GLint) (VB.Color[j1][1] * CC.GreenScale) << 8;
      b0 = (GLint) (VB.Color[j0][2] * CC.BlueScale) << 8;
      b1 = (GLint) (VB.Color[j1][2] * CC.BlueScale) << 8;
      a0 = (GLint) (VB.Color[j0][3] * CC.AlphaScale) << 8;
      a1 = (GLint) (VB.Color[j1][3] * CC.AlphaScale) << 8;
      if (len>1) {
         GLint n = len-1;
         dr = (r1-r0) / n;
         dg = (g1-g0) / n;
         db = (b1-b0) / n;
         da = (a1-a0) / n;
      }
      else {
         dr = dg = db = da = 0;
      }

      /* update span bounds */
      for (j=0;j<len;j++) {
	 GLfloat x = ex[j];
	 GLint y = ey[j];
	 if (y>=0 && y<MAX_HEIGHT) {
	    if (x < flx[y]) {
	       flx[y] = x;
	       lr[y] = r0 >> 8;
	       lg[y] = g0 >> 8;
	       lb[y] = b0 >> 8;
	       la[y] = a0 >> 8;
	    }
	    if (x > frx[y]) {
	       frx[y] = x;
	       rr[y] = r0 >> 8;
	       rg[y] = g0 >> 8;
	       rb[y] = b0 >> 8;
	       ra[y] = a0 >> 8;
	    }
	 }
         r0 += dr;   g0 += dg;   b0 += db;   a0 += da;
      }
   }

   /* process spans */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint zspan[MAX_WIDTH];
	 GLubyte red[MAX_WIDTH];
	 GLubyte green[MAX_WIDTH];
	 GLubyte blue[MAX_WIDTH];
	 GLubyte alpha[MAX_WIDTH];
	 GLubyte mask[MAX_WIDTH];
	 GLint z0, z1, *zptr;
         GLint r, g, b, a, dr, dg, db, da;
         GLuint passed = 0;

	 /* interpolate z */
	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 GL_INTERPOLATE_I( len, z0, z1, zspan );

         /* setup for color interpolation */
         r = lr[y] << 8;
         g = lg[y] << 8;
         b = lb[y] << 8;
         a = la[y] << 8;
         if (len>1) {
            dr = ((rr[y] << 8) - r) / (len-1);
            dg = ((rg[y] << 8) - g) / (len-1);
            db = ((rb[y] << 8) - b) / (len-1);
            da = ((ra[y] << 8) - a) / (len-1);
         }
         else {
            dr = dg = db = da = 0;
         }

	 /* do depth test */
	 zptr = CC.DepthBuffer + y * CC.BufferWidth + xmin;
	 for (i=0;i<len;i++) {
	    if (zspan[i]<zptr[i]) {
	       zptr[i] = zspan[i];
	       mask[i] = 1;
               red[i]   = r >> 8;
               green[i] = g >> 8;
               blue[i]  = b >> 8;
               alpha[i] = a >> 8;
               passed++;
	    }
	    else {
	       mask[i] = 0;
	    }
            r += dr;  g += dg;  b += db;  a += da;
	 }
	 /* write pixels */
         if (passed>0) {
            (*DD.write_color_span)( len, xmin, y,
                                    red, green, blue, alpha, mask );
         }
      }
   }
}



static void textured_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLint i, j, y;
   GLint ymin, ymax;
   GLfloat leyez[MAX_HEIGHT], reyez[MAX_HEIGHT];

   /* find min and max of window coordinate Y values */
   {
      GLfloat min = 1.0e10;
      GLfloat max = -1.0e10;
      if (n==3) {
         GLfloat winy;
         winy = VB.Win[vlist[0]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[1]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
         winy = VB.Win[vlist[2]][1];
         if (winy > max)  max = winy;
         if (winy < min)  min = winy;
      }
      else {
         for (i=0;i<n;i++) {
            GLfloat winy = VB.Win[vlist[i]][1];
            if (winy > max)  max = winy;
            if (winy < min)  min = winy;
         }
      }
      ymin = (GLint) min;
      ymin = CLAMP( ymin, 0, MAX_HEIGHT-1 ) ;
      ymax = (GLint) max;
      ymax = CLAMP( ymax, 0, MAX_HEIGHT-1 ) ;
   }

   /* init edge bounds */
   for (y=ymin;y<=ymax;y++) {
      flx[y] = (GLfloat) (MAX_WIDTH+1);
      frx[y] = -1.0;
   }

   /* process edges to compute bounds */
   for (i=0;i<n;i++) {
      GLuint j0, j1, len;
      DEFARRAY( GLfloat, ex, EDGEMAX );  /* x in win coords */
      DEFARRAY( GLint, ey, EDGEMAX );    /* y in win coords */
      DEFARRAY( GLfloat, ez, EDGEMAX );  /* z in eye coords */
      DEFARRAY( GLfloat, es, EDGEMAX );  /* texture s,t */
      DEFARRAY( GLfloat, et, EDGEMAX );
      GLint r0, g0, b0, a0, r1, g1, b1, a1, dr, dg, db, da;


      j0 = (i==0) ? vlist[n-1] : vlist[i-1];
      j1 = vlist[i];

      /* compute edge pixels */
      len = gl_polygon_edge( VB.Win[j0][0], VB.Win[j0][1],
                             VB.Win[j1][0], VB.Win[j1][1],
                             ex, ey );

      /* setup for edge color interpolation */
      r0 = (GLint) (VB.Color[j0][0] * CC.RedScale) << 8;
      r1 = (GLint) (VB.Color[j1][0] * CC.RedScale) << 8;
      g0 = (GLint) (VB.Color[j0][1] * CC.GreenScale) << 8;
      g1 = (GLint) (VB.Color[j1][1] * CC.GreenScale) << 8;
      b0 = (GLint) (VB.Color[j0][2] * CC.BlueScale) << 8;
      b1 = (GLint) (VB.Color[j1][2] * CC.BlueScale) << 8;
      a0 = (GLint) (VB.Color[j0][3] * CC.AlphaScale) << 8;
      a1 = (GLint) (VB.Color[j1][3] * CC.AlphaScale) << 8;
      if (len>1) {
         GLint n = len-1;
         dr = (r1-r0) / n;
         dg = (g1-g0) / n;
         db = (b1-b0) / n;
         da = (a1-a0) / n;
      }
      else {
         dr = dg = db = da = 0;
      }

      gl_interp_texcoords( len, VB.Eye[j0][2], VB.Eye[j1][2],
			   VB.Win[j0][2] * MAX_DEPTH, VB.Win[j1][2] * MAX_DEPTH,
			   VB.TexCoord[j0][0], VB.TexCoord[j1][0],
                           VB.TexCoord[j0][1], VB.TexCoord[j1][1],
			   es, et, ez );

      /* update span bounds */
      for (j=0;j<len;j++) {
	 register GLfloat x = ex[j];
	 register GLint y = ey[j];

	 if (y>=0 && y<MAX_HEIGHT) {
	    /* update left and right span bounds */
	    if (x < flx[y]) {
	       flx[y] = x;
	       lr[y] = r0 >> 8;
	       lg[y] = g0 >> 8;
	       lb[y] = b0 >> 8;
	       la[y] = a0 >> 8;
	       ls[y] = es[j];
	       lt[y] = et[j];
	       leyez[y] = ez[j];
	    }
	    if (x > frx[y]) {
	       frx[y] = x;
	       rr[y] = r0 >> 8;
	       rg[y] = g0 >> 8;
	       rb[y] = b0 >> 8;
	       ra[y] = a0 >> 8;
	       rs[y] = es[j];
	       rt[y] = et[j];
	       reyez[y] = ez[j];
	    }
	 }
         r0 += dr;   g0 += dg;   b0 += db;   a0 += da;
      }
      UNDEFARRAY( ex );
      UNDEFARRAY( ey );
      UNDEFARRAY( ez );
      UNDEFARRAY( es );
      UNDEFARRAY( et );
   }

   /* process spans */
   for (y=ymin;y<=ymax;y++) {
      GLint xmin = (GLint) (flx[y] + 0.5);
      GLint xmax = (GLint) (frx[y] - 0.5);
      GLint len = xmax-xmin+1;
      if (len>0) {
	 GLint z0, z1;
	 GLint zspan[MAX_WIDTH];
	 GLubyte red[MAX_WIDTH];
	 GLubyte green[MAX_WIDTH];
	 GLubyte blue[MAX_WIDTH];
	 GLubyte alpha[MAX_WIDTH];
	 GLfloat s[MAX_WIDTH], t[MAX_WIDTH];

	 /* interpolate z, colors and tex coords */
	 z0 = gl_compute_z( flx[y]+0.5F, (GLfloat) y + 0.5F );
	 z1 = gl_compute_z( frx[y]-0.5F, (GLfloat) y + 0.5F );
	 GL_INTERPOLATE_I( len, z0, z1, zspan );
	 GL_INTERPOLATE_4UB( len,
                             lr[y], rr[y], red,
                             lg[y], rg[y], green,
                             lb[y], rb[y], blue,
                             la[y], ra[y], alpha );

	 gl_interp_texcoords( len, leyez[y], reyez[y],
			      (GLfloat) z0, (GLfloat) z1,
			      ls[y], rs[y], lt[y], rt[y],
			      s, t, NULL );

	 gl_write_texture_span( len, xmin, y, zspan,  s, t,
				red, green, blue, alpha, GL_POLYGON );
      }
   }
}




/*
 * Render a polygon whose front or back rendering modes are point or line.
 */
static void unfilled_polygon( GLuint n, GLuint vlist[], GLuint pv )
{
   GLuint i, j, j0, j1;
   GLuint facing = (VB.Color==VB.Bcolor);   /* 0=front, 1=back */

   CC.StippleCounter = 0;  /* in case we're drawing polygon outline */

   if (facing==0) {
      /* Front */
      if (CC.Polygon.FrontMode==GL_POINT) {
	 for (i=0;i<n;i++) {
	    j = vlist[i];
	    if (VB.Edgeflag[j]) {
	       (*CC.PointsFunc)( j, j );
	    }
	 }
      }
      else if (CC.Polygon.FrontMode==GL_LINE) {
	 for (i=0;i<n;i++) {
	    j0 = (i==0) ? vlist[n-1] : vlist[i-1];
	    j1 = vlist[i];
	    if (VB.Edgeflag[j0]) {
	       (*CC.LineFunc)( j0, j1, pv );
	    }
	 }
      }
      else {
	 (*CC.AuxPolygonFunc)( n, vlist, pv );
      }
   }
   else {
      /* Back */
      if (CC.Polygon.BackMode==GL_POINT) {
	 for (i=0;i<n;i++) {
	    j = vlist[i];
	    if (VB.Edgeflag[j]) {
	       (*CC.PointsFunc)( j, j );
	    }
	 }
      }
      else if (CC.Polygon.BackMode==GL_LINE) {
	 for (i=0;i<n;i++) {
	    j0 = (i==0) ? vlist[n-1] : vlist[i-1];
	    j1 = vlist[i];
	    if (VB.Edgeflag[j0]) {
	       (*CC.LineFunc)( j0, j1, pv );
	    }
	 }
      }
      else {
	 (*CC.AuxPolygonFunc)( n, vlist, pv );
      }
   }
}



/*
 * Determine which polygon rendering function to use given the current
 * rendering context.
 */
void gl_set_polygon_function( void )
{
   if (CC.RenderMode==GL_RENDER) {
      CC.PolygonFunc = (*DD.get_polygon_func)();
      if (CC.PolygonFunc) {
         /* Device Driver will draw the polygon */
      }
      else if (CC.Texture.Enabled) {
	 /* textured */
	 CC.PolygonFunc = textured_polygon;
      }
      else if (CC.RGBAflag
	       && CC.Color.ColorMask==0xf
	       && CC.RasterMask==DEPTH_BIT
	       && CC.Depth.Func==GL_LESS
	       && CC.Depth.Mask==GL_TRUE
	       && CC.Polygon.StippleFlag==GL_FALSE
	       && CC.Texture.Enabled==0
	       && CC.ClipSpans==GL_FALSE) {
	 /* Common cases */
         if (CC.Light.ShadeModel==GL_SMOOTH) {
            CC.PolygonFunc = fast_smooth_rgba_z_polygon;
         }
         else {
            CC.PolygonFunc = fast_flat_rgba_z_polygon;
         }
      }
      else {
	 if (CC.Light.ShadeModel==GL_SMOOTH) {
	    /* smooth shaded, no texturing, stippled or some raster ops */
	    CC.PolygonFunc = CC.RGBAflag ? smooth_rgba_polygon : smooth_ci_polygon;
	 }
	 else {
	    /* flat shaded, no texturing, stippled or some raster ops */
	    CC.PolygonFunc = CC.RGBAflag ? flat_rgba_polygon : flat_ci_polygon;
	 }
      }

      /* PolygonMode */
      if (CC.Polygon.Unfilled) {
	 /* front or back are to be rendered as points or lines */
	 if (!CC.PointsFunc) {
	    gl_set_point_function();
	 }
	 if (!CC.LineFunc) {
	    gl_set_line_function();
	 }
	 CC.AuxPolygonFunc = CC.PolygonFunc;
	 CC.PolygonFunc = unfilled_polygon;
      }
   }
   else if (CC.RenderMode==GL_FEEDBACK) {
      CC.PolygonFunc = feedback_polygon;
   }
   else {
      /* GL_SELECT mode */
      CC.PolygonFunc = select_polygon;
   }
}


