ftp.nice.ch/Attic/openStep/developer/resources/Mesa3DFramework.s.tgz#/GL/Mesa.subproj/fxmesa.c

This is fxmesa.c in view mode; [Download] [Up]

/* fxmesa.c - 3Dfx VooDoo/Mesa interface */

/*
 * 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.
 *
 *
 * V0.17 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         optimized the bitmap support (66% faster)
 *         tested with the Mesa 2.3beta2
 *
 *         Diego Picciani (d.picciani@novacomp.it) Nova Computer s.r.l.
 *         solved a problem with the drawbitmap() and the Voodoo Rush (GR_ORIGIN_LOWER_LEFT did not work with the Stingray)
 *
 *         Brian Paul (brianp@elastic.avid.com) Avid Technology
 *         linux stuff
 *         general code clean-up
 *         added attribList parameter to fxMesaCreateContext()
 *         single buffering works now
 *         VB colors are now GLubytes, removed ColorShift stuff.
 *
 *         Paul Metzger
 *         linux stuff
 *
 * V0.16 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         written the quadfunc support (no performance improvement)
 *         written the support for the new Mesa 2.3beta1 driver interface (Wow ! It is faaaster)
 *         rewritten the glBitmap support for the Glide 2.3 (~35% slower !)
 *         written the glBitmap support for the most common case (fonts)
 *
 *         Jack Palevich
 *         Glide 2.3 porting
 *
 *         Diego Picciani (d.picciani@novacomp.it) Nova Computer s.r.l.
 *         extended the fxMesaCreateContext() and fxMesaCreateBestContext() functions in order to support also the Voodoo Rush
 *         tested with the Hercules Stingray 128/3D (The rendering in a window works !)
 *
 * V0.15 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         written the GL_LUMINANCE_ALPHA support
 *         written the GL_ALPHA support
 *         written the GL_LUMINANCE support
 *         now SETUP correctly set color for mono color sequences
 *         written the 9x1,10x1,...,1x9,1x10,... texture map ratio support
 *         written the no square texture map support
 *         the fog table is no more rebuilt inside setup_fx_units() each time
 *
 *         Henri Fousse (arnaud@pobox.oleane.com) Thomson Training & Simulation
 *         written (not yet finished: no texture mapping) support for glOrtho
 *         some change to setup functions
 *         the fog support is now fully compatible with the standard OpenGL
 *         rewritten several parts of the driver in order to take advantage of meshes (40% faster !!!)
 *
 * V0.14 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         now glAlphaFunc() works
 *         now glDepthMask() works
 *         solved a mipmap problem when using more than one texture
 *         moved ti, texid and wscale inside the fxMesaContext (now we can easy support more ctx and more boards)
 *         the management of the fxMesaContext was completly broken !
 *         solved several problems about Alpha and texture Alpha
 *         4 (RGBA) texture channels supported
 *         setting the default color to white
 *
 *         Henri Fousse (arnaud@pobox.oleane.com) Thomson Training & Simulation
 *         small change to fxMesaCreateContext() and fxMesaMakeCurrent()
 *         written the fog support
 *         setting the default clear color to black
 *         written cleangraphics() for the onexit() function
 *         written fxMesaCreateBestContext()
 *
 * V0.13 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         now glBlendFunc() works for all glBlendFunc without DST_ALPHA (because the alpha buffer is not yet implemented) 
 *         now fxMesaCreateContext() accept resolution and refresh rate
 *         fixed a bug for texture mapping: the w (alias z) must be set also without depth buffer
 *         fixed a bug for texture image with width!=256
 *         written texparam()
 *         written all point, line and triangle functions for all possible supported contexts and
 *         the driver is slight faster with points, lines and small triangles
 *         fixed a small bug in fx/fxmesa.h (glOrtho)
 *
 * V0.12 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         glDepthFunc supported
 *         introduced a trick to discover the far plane distance (see fxMesaSetFar and fx/fxmesa.h)
 *         now the wbuffer works with homogeneous coordinate (and it doesn't work with a glOrtho projection :)
 *         solved several problems with homogeneous coordinate and texture mapping
 *         fixed a bug in all line functions
 *         fixed a clear framebuffer bug
 *         solved a display list/teximg problem (but use glBindTexture: it is several times faster)
 *
 * V0.11 - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         introduced texture mapping support (not yet finished !)
 *         tested with Mesa2.2b6
 *         the driver is faster 
 *         written glFlush/glFinish
 *         the driver print a lot of info about the Glide lib
 *
 * v0.1  - David Bucciarelli (tech.hmw@plus.it) Humanware s.r.l.
 *         Initial revision
 *
 */

#ifdef FX


#ifdef __WIN32__
#include <windows.h>  /* is this really needed? */
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "context.h"
#include "macros.h"
#include "matrix.h"
#include "texture.h"
#include "types.h"
#include "vb.h"
#include "xform.h"
#include "GL/fxmesa.h"


#define MAXNUM_TEX	128

#define FXCOLOR(r,g,b,a) (( ((unsigned int)(a))<<24 )|( ((unsigned int)(b))<<16 )|( ((unsigned int)(g))<<8 )|(r))

#define FUNC_DEPTH	0x01
#define FUNC_SMOOTH	0x02
#define FUNC_TEX_DECAL	0x04
#define FUNC_TEX_MOD	0x08

typedef struct {
   GrLOD_t smallLOD; 
   GrLOD_t largeLOD;
   float sscale,tscale;
   int levelsdefined;
} texinfo;

struct fx_mesa_context {
   GLcontext *gl_ctx;             /* the core Mesa context */
   GLvisual *gl_vis;              /* describes the color buffer */
   GLframebuffer *gl_buffer;      /* the ancillary buffers */

   GLint width, height;           /* size of color buffer */
   GLboolean double_buffer;

   GrBuffer_t currentfb;

   GrColor_t color;
   GrColor_t clearc;
   GrAlpha_t cleara;

   GrMipMapId_t texid[MAXNUM_TEX];
   texinfo ti[MAXNUM_TEX];
   int currenttex;

   float wscale,nearval,farval;

   GLenum fogtablemode;
   GLfloat fogdensity;
   GrFog_t fogtable[64];

   GrVertex gwin[VB_SIZE];
};

fxMesaContext CurrentfxMesaCtx=NULL;



/**********************************************************************/
/*****                 Miscellaneous functions                    *****/
/**********************************************************************/

/* return buffer size information */
static void buffer_size(GLcontext *ctx, GLuint *width, GLuint *height)
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: buffer_size(...)\n");
#endif

   *width=fxMesa->width;
   *height=fxMesa->height;
}


/* Set current drawing color */
static void set_color(GLcontext *ctx, GLubyte red, GLubyte green,
                       GLubyte blue, GLubyte alpha )
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: set_color(%d,%d,%d,%d)\n",red,green,blue,alpha);
#endif

   fxMesa->color=FXCOLOR(red,green,blue,alpha);
}


/* implements glClearColor() */
static void clear_color(GLcontext *ctx, GLubyte red, GLubyte green,
                         GLubyte blue, GLubyte alpha )
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: clear_color(%d,%d,%d,%d)\n",red,green,blue,alpha);
#endif
 
   fxMesa->clearc=FXCOLOR(red,green,blue,255);
   fxMesa->cleara=alpha;
}


/* clear the frame buffer */
static void clear(GLcontext *ctx, GLboolean all,
                   GLint x, GLint y, GLint width, GLint height )
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: clear(%d,%d,%d,%d)\n",x,y,width,height);
#endif

   grDepthMask(FXFALSE);

   grBufferClear(fxMesa->clearc,fxMesa->cleara,GR_WDEPTHVALUE_FARTHEST);

   if(ctx->Depth.Test && ctx->Depth.Mask)
      grDepthMask(FXTRUE);
}


/*  set the buffer used in double buffering */
static GLboolean set_buffer(GLcontext *ctx, GLenum mode )
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
#if defined(DEBUG_FXMESA)
   printf("fxmesa: set_buffer(%d)\n",mode);
#endif

   if (fxMesa->double_buffer) {
      if(mode==GL_FRONT)
         fxMesa->currentfb = GR_BUFFER_FRONTBUFFER;
      else if(mode==GL_BACK)
         fxMesa->currentfb = GR_BUFFER_BACKBUFFER;
      else
         return GL_FALSE;
   }
   else {
      if(mode==GL_FRONT)
         fxMesa->currentfb = GR_BUFFER_FRONTBUFFER;
      else
         return GL_FALSE;
   }

   grRenderBuffer(fxMesa->currentfb);
   return GL_TRUE;
}


static GLboolean drawbitmap(GLcontext *ctx, GLsizei width, GLsizei height,
                            GLfloat xorig, GLfloat yorig,
                            GLfloat xmove, GLfloat ymove,
                            const struct gl_image *bitmap)
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
   FxU16 *p;
   GrLfbInfo_t info;
   GLubyte *pb;
   int x,y;
   GLint r,g,b,a,px,py,scrwidth,scrheight,stride;
   FxU16 color;

#define ISCLIPPED(rx) ( ((rx)<0) || ((rx)>=scrwidth) )
#define DRAWBIT(i) {		\
      if(!ISCLIPPED(x+px))	\
         if( (*pb) & (1<<(i)) )	\
            (*p)=color;		\
      p++;			\
      x++;			\
      if(x>=width) {		\
         pb++;			\
         break;			\
      }				\
   }

   scrwidth=fxMesa->width;
   scrheight=fxMesa->height;

   px=(GLint)((ctx->Current.RasterPos[0]-xorig)+0.0F);
   py=(GLint)((ctx->Current.RasterPos[1]-yorig)+0.0F);

   if((px>=scrwidth) || (px+width<=0) || (py>=scrheight) || (py+height<=0))
      return GL_TRUE;

   pb=(GLubyte *)bitmap->Data;

   if(py<0) {
      pb+=(bitmap->Height*(-py)) >> (3+1);
      height+=py;
      py=0;
   }

   if(py+height>=scrheight)
      height-=(py+height)-scrheight;

   info.size=sizeof(info);
   if(!grLfbLock(GR_LFB_WRITE_ONLY,
                 fxMesa->currentfb,
                 GR_LFBWRITEMODE_565,
                 GR_ORIGIN_UPPER_LEFT,
                 FXFALSE,
                 &info)) {
#ifndef FX_SILENT
      fprintf(stderr,"3Dfx Driver: error locking the linear frame buffer\n");
#endif
      return GL_TRUE;
   }

   r=(GLint)(ctx->Current.RasterColor[0]*ctx->Visual->RedScale);
   g=(GLint)(ctx->Current.RasterColor[1]*ctx->Visual->GreenScale);
   b=(GLint)(ctx->Current.RasterColor[2]*ctx->Visual->BlueScale);
   a=(GLint)(ctx->Current.RasterColor[3]*ctx->Visual->AlphaScale);
   color=(FxU16)
      ( ((FxU16)0xf8 & r) <<(11-3))  |
      ( ((FxU16)0xfc & g) <<(5-3+1)) |
      ( ((FxU16)0xf8 & b) >> 3);

   stride=info.strideInBytes>>1;

   /* This code is a bit slow... */

   for(y=0;y<height;y++) {
      p=((FxU16 *)info.lfbPtr)+px+((scrheight-(y+py))*stride);

      for(x=0;;) {
         DRAWBIT(7);	DRAWBIT(6);	DRAWBIT(5);	DRAWBIT(4);
         DRAWBIT(3);	DRAWBIT(2);	DRAWBIT(1);	DRAWBIT(0);
         pb++;
      }
   }

   grLfbUnlock(GR_LFB_WRITE_ONLY,fxMesa->currentfb);

#undef ISCLIPPED
#undef DRAWBIT

   return GL_TRUE;
}


/************************************************************************/
/************************************************************************/
/************************************************************************/

#define GOURAUD_ENABLED 0x1
#define TEXTURE_ENABLED 0x2
#define FOG_ENABLED 0x4
#define WBUFFER_ENABLED 0x8
#define ZBUFFER_ENABLED 0x10

#define GOURAUD(v) {\
	(v)->r = (float) (VB->Color[i][0]); \
	(v)->g = (float) (VB->Color[i][1]); \
	(v)->b = (float) (VB->Color[i][2]); \
	(v)->a = (float) (VB->Color[i][3]); }

#define WBUFFER(v) { (v)->oow = wscale/VB->Clip[i][3]; }

#define TEXTURE(v)  { \
	(v)->tmuvtx[0].sow = sscale*VB->TexCoord[i][0]*(v)->oow; \
	(v)->tmuvtx[0].tow = tscale*VB->TexCoord[i][1]*(v)->oow; \
}

#define ZBUFFER(v) { (v)->oow = 1.0/VB->Win[i][2]; }

#define NOP(v) 

#ifdef __WIN32__
#define SETUP( gouraud, texture, depth ) {				\
	register unsigned int i;					\
	register struct vertex_buffer *VB = ctx->VB;			\
	fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;		\
	register GrVertex *GVB = &fxMesa->gwin[vstart];			\
	register float wscale = fxMesa->wscale; 			\
	register float sscale = fxMesa->ti[fxMesa->currenttex].sscale;	\
	register float tscale = fxMesa->ti[fxMesa->currenttex].tscale;	\
	unsigned int cw,ocw;						\
									\
	__asm								\
	{								\
		__asm fstcw ocw						\
		__asm fwait						\
		__asm mov eax, ocw					\
		__asm and eax, 0fffffcffh				\
		__asm mov cw, eax					\
		__asm fldcw cw						\
		__asm fwait						\
	}								\
	for ( i = vstart; i < vend ; i++, GVB++ )			\
	{								\
		GVB->x = (VB->Win[i][0] + 524288.0f) - 524288.0f;	\
		GVB->y = (VB->Win[i][1] + 524288.0f) - 524288.0f;	\
	}								\
	__asm								\
	{								\
		__asm fldcw ocw						\
		__asm fwait						\
	}								\
	GVB = &fxMesa->gwin[vstart];					\
	for ( i = vstart; i < vend ; i++, GVB++ )			\
	{								\
		gouraud(GVB);						\
		depth(GVB);						\
		texture(GVB);						\
	}								\
}
#else
#define SETUP( gouraud, texture, depth ) {				\
	register unsigned int i;					\
	register struct vertex_buffer *VB = ctx->VB;			\
	fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx; 		\
	register GrVertex *GVB = &fxMesa->gwin[vstart];			\
	register float wscale = fxMesa->wscale; 			\
	register float sscale = fxMesa->ti[fxMesa->currenttex].sscale;	\
	register float tscale = fxMesa->ti[fxMesa->currenttex].tscale;	\
									\
	for ( i = vstart; i < vend ; i++, GVB++ )			\
	{								\
		/* trunc (x,y) to multiple of 1/16 */			\
		GVB->x = ((int)(VB->Win[i][0]*16.0f))*(1.0f/16.0f);	\
		GVB->y = ((int)(VB->Win[i][1]*16.0f))*(1.0f/16.0f);	\
		gouraud(GVB);						\
		depth(GVB);						\
		texture(GVB);						\
	}								\
}
#endif

static void setup( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( NOP, NOP, NOP );
}
	
static void setupG( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( GOURAUD, NOP, NOP );
}

static void setupT( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( NOP, TEXTURE, WBUFFER );
}
	
static void setupGT( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( GOURAUD, TEXTURE, WBUFFER );
}
	
static void setupW( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( NOP, NOP, WBUFFER );
}
	
static void setupGW( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( GOURAUD, NOP, WBUFFER );
}

static void setupZ( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( NOP, NOP, ZBUFFER );
}
	
static void setupGZ( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( GOURAUD, NOP, ZBUFFER );
}

static void setupTZ( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( NOP, TEXTURE, ZBUFFER );
}
	
static void setupGTZ( GLcontext *ctx, GLuint vstart, GLuint vend )
{
	SETUP( GOURAUD, TEXTURE, ZBUFFER );
}
	
typedef void (*setup_func)(GLcontext *, GLuint, GLuint);

setup_func fxSetupFuncs[] = {
	setup,
	setupG,
	setupT,
	setupGT,
	setupW,
	setupGW,
	setupT,
	setupGT,
	setupW,
	setupGW,
	setupT,
	setupGT,
	setupW,
	setupGW,
	setupT,
	setupGT,
	setupZ,
	setupGZ,
	setupTZ,
	setupGTZ,
	setupZ,
	setupGZ,
	setupTZ,
	setupGTZ
};

setup_func 
choose_setup_function( GLcontext *ctx )
{
	unsigned int setupIndex = 0;

	if ( ctx->Light.ShadeModel == GL_SMOOTH && !ctx->Light.Model.TwoSide )
		setupIndex |= GOURAUD_ENABLED;
	if ( ctx->Texture.Enabled )
		setupIndex |= TEXTURE_ENABLED;
	if ( ctx->Fog.Enabled )
		setupIndex |= FOG_ENABLED;
	if ( ctx->Depth.Test ) {
	  if ( ctx->ProjectionMatrix[15] == 0.0f )
	    setupIndex |= WBUFFER_ENABLED;
	  else
	    setupIndex |= ZBUFFER_ENABLED;
	}

	return fxSetupFuncs[setupIndex];
}

#undef GOURAUD
#define GOURAUD(v) { \
	fxMesa->gwin[(v)].r = (float) VB->Color[(v)][0]; \
	fxMesa->gwin[(v)].g = (float) VB->Color[(v)][1]; \
	fxMesa->gwin[(v)].b = (float) VB->Color[(v)][2]; \
	fxMesa->gwin[(v)].a = (float) VB->Color[(v)][3]; }

static void 
fxPoint(GLcontext *ctx, GLuint first, GLuint last)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   unsigned int i;

   if(ctx->MonoPixels)
      grConstantColorValue(fxMesa->color);

   for ( i = first; i < last ; i++ )
      grDrawPoint( &fxMesa->gwin[i] );
}


points_func 
choose_points_function( GLcontext *ctx )
{
   return fxPoint;
}

static void 
fxLineSmooth(GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   grDrawLine( &fxMesa->gwin[v1], &fxMesa->gwin[v2] );
}

static void 
fxLineSmoothTwoSide(GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   struct vertex_buffer *VB = ctx->VB;
   GOURAUD(v1); 
   GOURAUD(v2); 
   grDrawLine( &fxMesa->gwin[v1], &fxMesa->gwin[v2] );
}

static void 
fxLineFlat(GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   GLubyte *Color = ctx->VB->Color[pv];

   grConstantColorValue(FXCOLOR( Color[0], Color[1], Color[2], Color[3]));
   grDrawLine( &fxMesa->gwin[v1], &fxMesa->gwin[v2] );
}

line_func 
choose_line_function( GLcontext *ctx )
{
   if ( ctx->Light.ShadeModel == GL_SMOOTH )
      if ( ctx->Light.Model.TwoSide )
         return fxLineSmoothTwoSide;
      else
         return fxLineSmooth;
   else
      return fxLineFlat;
}

/************************************************************************/
/*********************** Triangle functions *****************************/
/************************************************************************/

static void 
fxTriangleSmooth(GLcontext *ctx, GLuint v1, GLuint v2, GLuint v3, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   grDrawTriangle( &fxMesa->gwin[v1], &fxMesa->gwin[v2], &fxMesa->gwin[v3] );
}

static void 
fxTriangleSmoothTwoSide(GLcontext *ctx, GLuint v1, GLuint v2, GLuint v3, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   struct vertex_buffer *VB = ctx->VB;
   GOURAUD(v1); 
   GOURAUD(v2); 
   GOURAUD(v3); 
   grDrawTriangle( &fxMesa->gwin[v1], &fxMesa->gwin[v2], &fxMesa->gwin[v3] );
}

static void 
fxTriangleFlat( GLcontext *ctx, GLuint v1, GLuint v2, GLuint v3, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   GLubyte *Color = ctx->VB->Color[pv];
   grConstantColorValue(FXCOLOR( Color[0], Color[1], Color[2], Color[3]));
   grDrawTriangle( &fxMesa->gwin[v1], &fxMesa->gwin[v2], &fxMesa->gwin[v3] );
}

triangle_func 
choose_triangle_function( GLcontext *ctx )
{
   if ( ctx->Light.ShadeModel == GL_SMOOTH )
      if ( ctx->Light.Model.TwoSide )
         return fxTriangleSmoothTwoSide;
      else
         return fxTriangleSmooth;
   else
      return fxTriangleFlat;
}

/************************************************************************/
/************************* Quads functions ******************************/
/************************************************************************/

static void 
fxQuadSmooth(GLcontext *ctx, GLuint v1, GLuint v2, GLuint v3, GLuint v4, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   GrVertex *grv2,*grv3;
   grv2=&fxMesa->gwin[v2];
   grv3=&fxMesa->gwin[v3];
   grDrawTriangle(&fxMesa->gwin[v1], grv2, grv3);
   grDrawTriangle(grv3, grv2, &fxMesa->gwin[v4]);
}

static void 
fxQuadSmoothTwoSide(GLcontext *ctx, GLuint v1, GLuint v2, GLuint v3, GLuint v4, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   struct vertex_buffer *VB = ctx->VB;
   GrVertex *grv2,*grv3;

   GOURAUD(v1); 
   GOURAUD(v2); 
   GOURAUD(v3); 
   GOURAUD(v4); 

   grv2=&fxMesa->gwin[v2];
   grv3=&fxMesa->gwin[v3];
	
   grDrawTriangle(&fxMesa->gwin[v1], grv2, grv3);
   grDrawTriangle(grv3, grv2, &fxMesa->gwin[v4]);
}

static void 
fxQuadFlat(GLcontext *ctx, GLuint v1, GLuint v2, GLuint v3,GLuint v4, GLuint pv)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;
   GLubyte *Color = ctx->VB->Color[pv];
   GrVertex *grv2,*grv3;

   grConstantColorValue(FXCOLOR( Color[0], Color[1], Color[2], Color[3]));

   grv2=&fxMesa->gwin[v2];
   grv3=&fxMesa->gwin[v3];
	
   grDrawTriangle(&fxMesa->gwin[v1], grv2, grv3);
   grDrawTriangle(grv3, grv2, &fxMesa->gwin[v4]);
}

quad_func 
choose_quad_function(GLcontext *ctx)
{
   if(ctx->Light.ShadeModel == GL_SMOOTH)
      if(ctx->Light.Model.TwoSide)
         return fxQuadSmoothTwoSide;
      else
         return fxQuadSmooth;
   else
      return fxQuadFlat;
}

/************************************************************************/
/**************** 3D depth buffer functions *****************************/
/************************************************************************/

/* this is a no-op, since the z-buffer is in hardware */
static void alloc_depth_buffer(GLcontext *ctx)
{
#if defined(DEBUG_FXMESA)
   printf("fxmesa: alloc_depth_buffer()\n");
#endif
}

static void clear_depth_buffer(GLcontext *ctx)
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: clear_depth_buffer()\n");
#endif

   if(ctx->Depth.Test && ctx->Depth.Mask) {
      grDepthMask(FXTRUE);
      grColorMask(FXFALSE,FXFALSE);

      /* I don't know how to convert ctx->Depth.Clear */
      grBufferClear(fxMesa->clearc,fxMesa->cleara,GR_WDEPTHVALUE_FARTHEST);

      grColorMask(FXTRUE,FXFALSE);
   }
}

/************************************************************************/
/************************************************************************/
/************************************************************************/

static void finish(GLcontext *ctx)
{
   grSstIdle();
}

static void flush(GLcontext *ctx)
{
   grSstIdle();
}

/************************************************************************/
/*************************** Texture Mapping ****************************/
/************************************************************************/

static void texenv(GLcontext *ctx, GLenum pname, const GLfloat *param)
{
	/* ;) */
}

static void texparam(GLcontext *ctx, GLenum target, GLuint texObject,
					 GLenum pname, const GLfloat *params)
{
   GLenum param=(GLenum)(GLint)params[0];
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
   GrMipMapId_t *texid=fxMesa->texid;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: texparam(%d)\n",texObject);
#endif

   if(target!=GL_TEXTURE_2D) {
#ifndef FX_SILENT
      fprintf(stderr,"3Dfx Driver: unsupported texture in texparam()\n");
#endif
      return;
   }

   switch(pname) {

      case GL_TEXTURE_MIN_FILTER:
         switch(param) {
            case GL_NEAREST:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     GR_MIPMAP_DISABLE,-1,-1,-1,-1,-1,
                                     GR_TEXTUREFILTER_POINT_SAMPLED,-1);
               break;
            case GL_LINEAR:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     GR_MIPMAP_DISABLE,-1,-1,-1,-1,-1,
                                     GR_TEXTUREFILTER_BILINEAR,-1);
               break;
            case GL_NEAREST_MIPMAP_NEAREST:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     GR_MIPMAP_NEAREST,-1,-1,-1,-1,-1,
                                     GR_TEXTUREFILTER_POINT_SAMPLED,-1);
               break;
            case GL_NEAREST_MIPMAP_LINEAR:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     GR_MIPMAP_NEAREST,-1,-1,-1,-1,-1,
                                     GR_TEXTUREFILTER_BILINEAR,-1);
               break;
            case GL_LINEAR_MIPMAP_LINEAR:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     GR_MIPMAP_NEAREST_DITHER,-1,-1,-1,-1,-1,
                                     GR_TEXTUREFILTER_BILINEAR,-1);
               break;
            default:
               break;
         }
         break;

      case GL_TEXTURE_MAG_FILTER:
         switch(param) {
            case GL_NEAREST:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     -1,-1,-1,-1,-1,-1,
                                     -1,GR_TEXTUREFILTER_POINT_SAMPLED);
               break;
            case GL_LINEAR:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,
                                     -1,-1,-1,-1,-1,-1,
                                     -1,GR_TEXTUREFILTER_BILINEAR);
               break;
            default:
               break;
         }
         break;

      case GL_TEXTURE_WRAP_S:
         switch(param) {
            case GL_CLAMP:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,-1,-1,
                                     -1,-1,GR_TEXTURECLAMP_CLAMP,-1,-1,-1);
               break;
            case GL_REPEAT:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,-1,-1,
                                     -1,-1,GR_TEXTURECLAMP_WRAP,-1,-1,-1);
               break;
            default:
               break;
         }
         break;

      case GL_TEXTURE_WRAP_T:
         switch(param) {
            case GL_CLAMP:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,-1,-1,-1,
                                     -1,-1,GR_TEXTURECLAMP_CLAMP,-1,-1);
               break;
            case GL_REPEAT:
               guTexChangeAttributes(texid[texObject],-1,-1,-1,-1,-1,-1,
                                     -1,-1,GR_TEXTURECLAMP_WRAP,-1,-1);
               break;
            default:
               break;
         }
         break;

      case GL_TEXTURE_BORDER_COLOR:
         /* TO DO */
         break;
      default:
         break;
   }

   guTexSource(texid[texObject]);
   fxMesa->currenttex=texObject;
}

static void texdel(GLcontext *ctx, GLuint texObject)
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

   fxMesa->texid[texObject]=GR_NULL_MIPMAP_HANDLE;
   fxMesa->ti[texObject].levelsdefined=0;

   /* TO DO: Free the texture memory */
}

static void texbind(GLcontext *ctx, GLenum target, GLuint texObject)
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: texbind(%d)\n",texObject);
#endif

   guTexSource(fxMesa->texid[texObject]);
   fxMesa->currenttex=texObject;
}

static int logbase2(int n)
{
   GLint i = 1;
   GLint log2 = 0;

   if (n<0) {
      return -1;
   }

   while ( n > i ) {
      i *= 2;
      log2++;
   }
   if (i != n) {
      return -1;
   }
   else {
      return log2;
   }
}

static int texgetinfo(int w, int h, GrLOD_t *lodlevel, GrAspectRatio_t *ar,
                      float *sscale, float *tscale,
                      int *wscale, int *hscale)
{
   static GrLOD_t lod[9]={GR_LOD_256,GR_LOD_128,GR_LOD_64,GR_LOD_32,
                          GR_LOD_16,GR_LOD_8,GR_LOD_4,GR_LOD_2,GR_LOD_1};
   int logw,logh,ws,hs;
   GrLOD_t l;
   GrAspectRatio_t aspectratio;
   float s,t;

   logw=logbase2(w);
   logh=logbase2(h);

	switch(logw-logh) {
		case 0:
			aspectratio=GR_ASPECT_1x1;
			l=lod[8-logw];
			s=t=255.0;
			ws=hs=1;
			break;
		case 1:
			aspectratio=GR_ASPECT_2x1;
			l=lod[8-logw];
			s=255.0;
			t=127.0;
			ws=1;
			hs=1;
			break;
		case 2:
			aspectratio=GR_ASPECT_4x1;
			l=lod[8-logw];
			s=255.0;
			t=63.0;
			ws=1;
			hs=1;
			break;
		case 3:
			aspectratio=GR_ASPECT_8x1;
			l=lod[8-logw];
			s=255.0;
			t=31.0;
			ws=1;
			hs=1;
			break;
		case 4:
			aspectratio=GR_ASPECT_8x1;
			l=lod[8-logw];
			s=255.0;
			t=31.0;
			ws=1;
			hs=2;
			break;
		case 5:
			aspectratio=GR_ASPECT_8x1;
			l=lod[8-logw];
			s=255.0;
			t=31.0;
			ws=1;
			hs=4;
			break;
		case 6:
			aspectratio=GR_ASPECT_8x1;
			l=lod[8-logw];
			s=255.0;
			t=31.0;
			ws=1;
			hs=8;
			break;
		case 7:
			aspectratio=GR_ASPECT_8x1;
			l=lod[8-logw];
			s=255.0;
			t=31.0;
			ws=1;
			hs=16;
			break;
		case 8:
			aspectratio=GR_ASPECT_8x1;
			l=lod[8-logw];
			s=255.0;
			t=31.0;
			ws=1;
			hs=32;
			break;
		case -1:
			aspectratio=GR_ASPECT_1x2;
			l=lod[8-logh];
			s=127.0;
			t=255.0;
			ws=1;
			hs=1;
			break;
		case -2:
			aspectratio=GR_ASPECT_1x4;
			l=lod[8-logh];
			s=63.0;
			t=255.0;
			ws=1;
			hs=1;
			break;
		case -3:
			aspectratio=GR_ASPECT_1x8;
			l=lod[8-logh];
			s=31.0;
			t=255.0;
			ws=1;
			hs=1;
			break;
		case -4:
			aspectratio=GR_ASPECT_1x8;
			l=lod[8-logh];
			s=31.0;
			t=255.0;
			ws=2;
			hs=1;
			break;
		case -5:
			aspectratio=GR_ASPECT_1x8;
			l=lod[8-logh];
			s=31.0;
			t=255.0;
			ws=4;
			hs=1;
			break;
		case -6:
			aspectratio=GR_ASPECT_1x8;
			l=lod[8-logh];
			s=31.0;
			t=255.0;
			ws=8;
			hs=1;
			break;
		case -7:
			aspectratio=GR_ASPECT_1x8;
			l=lod[8-logh];
			s=31.0;
			t=255.0;
			ws=16;
			hs=1;
			break;
		case -8:
			aspectratio=GR_ASPECT_1x8;
			l=lod[8-logh];
			s=31.0;
			t=255.0;
			ws=32;
			hs=1;
			break;
		default:
			return 0;
			break;
	}

	if(lodlevel)
		(*lodlevel)=l;

	if(ar)
		(*ar)=aspectratio;

	if(sscale)
		(*sscale)=s;

	if(tscale)
		(*tscale)=t;

	if(wscale)
		(*wscale)=ws;

	if(hscale)
		(*hscale)=hs;

	return 1;
}

static void texalloc(GLcontext *ctx, GLuint texObject, GLenum glformat, int w, int h)
{
   GrTextureFormat_t format;
   GrLOD_t l;
   GrAspectRatio_t aspectratio;
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
   GrMipMapId_t *texid=fxMesa->texid;
   texinfo *ti=fxMesa->ti;
   int wscale,hscale;

   texgetinfo(w,h,&l,&aspectratio,&(ti[texObject].sscale),&(ti[texObject].tscale),&wscale,&hscale);

   switch(glformat) {
      case GL_LUMINANCE:
      case GL_LUMINANCE8:
      case 1:
         format=GR_TEXFMT_INTENSITY_8;
         break;
      case GL_LUMINANCE_ALPHA:
      case GL_LUMINANCE8_ALPHA8:
      case 2:
         format=GR_TEXFMT_ALPHA_INTENSITY_88;
         break;
      case GL_ALPHA8:
      case GL_ALPHA:
         format=GR_TEXFMT_ALPHA_8;
         break;
      case GL_RGB:
      case GL_RGB8:
      case 3:
         format=GR_TEXFMT_RGB_565;
         break;
      case GL_RGBA:
      case GL_RGBA8:
      case 4:
         format=GR_TEXFMT_ARGB_4444;
         break;
      default:
         fprintf(stderr,"3Dfx Driver: wrong internalFormat in texalloc()\n");
         grGlideShutdown();
         exit(-1);
         break;
   }

   texid[texObject]=guTexAllocateMemory(GR_TMU0,GR_MIPMAPLEVELMASK_BOTH,
        w*wscale,h*hscale,
	format,GR_MIPMAP_NEAREST,GR_LOD_1,l,aspectratio,
	GR_TEXTURECLAMP_WRAP,GR_TEXTURECLAMP_WRAP,GR_TEXTUREFILTER_BILINEAR,
	GR_TEXTUREFILTER_BILINEAR,0.0,FXFALSE);

   if(texid[texObject]==GR_NULL_MIPMAP_HANDLE) {
      fprintf(stderr,"3Dfx Driver: out of texture memory !\n");
      grGlideShutdown();
      exit(-1);
   }

   ti[texObject].smallLOD=l; 
   ti[texObject].largeLOD=l;
   ti[texObject].levelsdefined=0;
}

static int istexsupported(GLenum target, GLint internalFormat,
                          const struct gl_texture_image *image)
{
   if(target!=GL_TEXTURE_2D)
      return GL_FALSE;

   switch(internalFormat) {
      case GL_LUMINANCE:
      case GL_LUMINANCE8:
      case 1:
      case GL_LUMINANCE_ALPHA:
      case GL_LUMINANCE8_ALPHA8:
      case 2:
      case GL_ALPHA8:
      case GL_ALPHA:
      case GL_RGB:
      case GL_RGB8:
      case 3:
      case GL_RGBA:
      case GL_RGBA8:
      case 4:
         break;
      default:
         return GL_FALSE;
   }

   if(image->Width>256)
      return GL_FALSE;

   if(image->Height>256)
      return GL_FALSE;

   if(!texgetinfo(image->Width,image->Height,NULL,NULL,NULL,NULL,NULL,NULL))
      return GL_FALSE;

   return GL_TRUE;
}

static unsigned short *texbuildimagemap(const struct gl_texture_image *image, GLint internalFormat)
{
   unsigned short *src,*srccpy;
   unsigned char r,g,b,a,l,*data,*srcb;
   int x,y,w,h,wscale,hscale,idx;

   texgetinfo(image->Width,image->Height,NULL,NULL,NULL,NULL,&wscale,&hscale);
   w=image->Width*wscale;
   h=image->Height*hscale;

   if(!(srccpy=src=(unsigned short *)malloc(sizeof(unsigned short)*w*h))) {
      fprintf(stderr,"3Dfx Driver: out of memory !\n");
      grGlideShutdown();
      exit(-1);
   }

   data=image->Data;
   switch(internalFormat) {
      case GL_ALPHA:
      case GL_ALPHA8:
      case GL_LUMINANCE:
      case GL_LUMINANCE8:
      case 1:
         srcb=(unsigned char *)src;
         for(y=0;y<h;y++)
            for(x=0;x<w;x++) {
               idx=(x/wscale+(y/hscale)*(w/wscale));
               srcb[x+y*w]=data[idx];
            }
         break;
      case GL_LUMINANCE_ALPHA:
      case GL_LUMINANCE8_ALPHA8:
      case 2:
         for(y=0;y<h;y++)
            for(x=0;x<w;x++) {
               idx=(x/wscale+(y/hscale)*(w/wscale))*2;
               l=data[idx];
               a=data[idx+1];

               src[x+y*w]=(unsigned short)
                  ( ((unsigned short) a) << 8) |
                  ( ((unsigned short) l));
            }
         break;
      case GL_RGB:
      case GL_RGB8:
      case 3:
         for(y=0;y<h;y++)
            for(x=0;x<w;x++) {
               idx=(x/wscale+(y/hscale)*(w/wscale))*3;
               r=data[idx];
               g=data[idx+1];
               b=data[idx+2];

               src[x+y*w]=(unsigned short)
                  ( ((unsigned short)0xf8 & r) <<(11-3))  |
                  ( ((unsigned short)0xfc & g) <<(5-3+1)) |
                  ( ((unsigned short)0xf8 & b) >> 3); 
            }
         break;
      case GL_RGBA:
      case GL_RGBA8:
      case 4:
         for(y=0;y<h;y++)
            for(x=0;x<w;x++) {
               idx=(x/wscale+(y/hscale)*(w/wscale))*4;
               r=data[idx];
               g=data[idx+1];
               b=data[idx+2];
               a=data[idx+3];

               src[x+y*w]=(unsigned short)
                  ( ((unsigned short)0xf0 & a) << 8) |
                  ( ((unsigned short)0xf0 & r) << 4) |
                  ((unsigned short)0xf0 & g)       |
                  ( ((unsigned short)0xf0 & b) >> 4); 
            }
         break;
      default:
         fprintf(stderr,"3Dfx Driver: wrong internalFormat in texbuildimagemap()\n");
         grGlideShutdown();
         exit(-1);
         break;
   }

   return srccpy;
}

static void teximg(GLcontext *ctx, GLenum target,
                   GLuint texObject, GLint level, GLint internalFormat,
                   const struct gl_texture_image *image)
{
   unsigned short *src,*srccpy;
   GrLOD_t lodlev;
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
   GrMipMapId_t *texid=fxMesa->texid;
   texinfo *ti=fxMesa->ti;

#if defined(DEBUG_FXMESA)
   printf("fxmesa: teximg(...,%d,%d,%d,%d...)\n",target,internalFormat,image->Width,image->Height);
#endif

   if(istexsupported(target,internalFormat,image)) {
      if(texid[texObject]==GR_NULL_MIPMAP_HANDLE)
         texalloc(ctx,texObject,image->Format,image->Width,image->Height);
      else {
         if(ti[texObject].levelsdefined & (1<<level)) {
            texdel(ctx,texObject);
            texalloc(ctx,texObject,image->Format,image->Width,image->Height);
         }
      }

      srccpy=src=texbuildimagemap(image,internalFormat);
		
      texgetinfo(image->Width,image->Height,&lodlev,NULL,NULL,NULL,NULL,NULL);

      if(lodlev>ti[texObject].smallLOD) {
         guTexChangeAttributes(texid[texObject],-1,-1,-1,-1,lodlev,-1,-1,-1,-1,-1,-1);
         ti[texObject].smallLOD=lodlev;
      }

      if(lodlev<ti[texObject].largeLOD) {
         guTexChangeAttributes(texid[texObject],-1,-1,-1,-1,-1,lodlev,-1,-1,-1,-1,-1);
         ti[texObject].largeLOD=lodlev;
      }

      guTexDownloadMipMapLevel(texid[texObject],lodlev,(const void**)&src);

      free(srccpy);

      ti[texObject].levelsdefined|=(1<<level);

      guTexSource(texid[texObject]);
      fxMesa->currenttex=texObject;
   }
#ifndef FX_SILENT
   else
      fprintf(stderr,"3Dfx Driver: unsupported texture in teximg()\n");
#endif

}

/************************************************************************/
/************************************************************************/
/************************************************************************/

static void SetNearFar( GLcontext *ctx, GLfloat n, GLfloat f)
{
   fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;

   if(fxMesa) {
      fxMesa->nearval=fabs(n);
      fxMesa->farval=fabs(f);
      fxMesa->wscale=fxMesa->farval/65535.0;

      /*
       * We need to update fog table because it depends on w 
       * and w is adjusted to the maximum range.
       */
      if(ctx->Fog.Enabled && ctx->Hint.Fog==GL_NICEST) {
         switch(ctx->Fog.Mode)	{
            case GL_LINEAR:
               guFogGenerateLinear(fxMesa->fogtable,
                                   ctx->Fog.Start/fxMesa->wscale,
                                   ctx->Fog.End /fxMesa->wscale); /* works ? */
               break;
            case GL_EXP:
               guFogGenerateExp(fxMesa->fogtable,
                                ctx->Fog.Density*fxMesa->wscale);
               break;
            case GL_EXP2:
               guFogGenerateExp2(fxMesa->fogtable,
                                 ctx->Fog.Density*fxMesa->wscale);
               break;
            default: /* That should never happen */
               break; 
         }
      }
   }
}

void fxMesaSetNearFar(GLfloat n, GLfloat f)
{
   SetNearFar(CurrentfxMesaCtx->gl_ctx, n, f);
}


static void setup_fx_units(GLcontext *ctx)
{
   fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;

   if(ctx->Color.BlendEnabled) {
      GrAlphaBlendFnc_t sfact,dfact;

      if(ctx->Light.ShadeModel==GL_SMOOTH)
         guAlphaSource(GR_ALPHASOURCE_ITERATED_ALPHA);
      else
         guAlphaSource(GR_ALPHASOURCE_CC_ALPHA);

      switch(ctx->Color.BlendSrc) {
         case GL_ZERO:
            sfact=GR_BLEND_ZERO;
            break;
         case GL_ONE:
            sfact=GR_BLEND_ONE;
            break;
         case GL_DST_COLOR:
            sfact=GR_BLEND_DST_COLOR;
            break;
         case GL_ONE_MINUS_DST_COLOR:
            sfact=GR_BLEND_ONE_MINUS_DST_COLOR;
            break;
         case GL_SRC_ALPHA:
            sfact=GR_BLEND_SRC_ALPHA;
            break;
         case GL_ONE_MINUS_SRC_ALPHA:
            sfact=GR_BLEND_ONE_MINUS_SRC_ALPHA;
            break;
         case GL_SRC_ALPHA_SATURATE:
         case GL_SRC_COLOR:
         case GL_ONE_MINUS_SRC_COLOR:
         case GL_DST_ALPHA:
         case GL_ONE_MINUS_DST_ALPHA:
            /* USELESS or TO DO */
            sfact=GR_BLEND_ONE;
            break;
         default:
            break;
      }

      switch(ctx->Color.BlendDst) {
         case GL_ZERO:
            dfact=GR_BLEND_ZERO;
            break;
         case GL_ONE:
            dfact=GR_BLEND_ONE;
            break;
         case GL_SRC_COLOR:
            dfact=GR_BLEND_SRC_COLOR;
            break;
         case GL_ONE_MINUS_SRC_COLOR:
            dfact=GR_BLEND_ONE_MINUS_SRC_COLOR;
            break;
         case GL_SRC_ALPHA:
            dfact=GR_BLEND_SRC_ALPHA;
            break;
         case GL_ONE_MINUS_SRC_ALPHA:
            dfact=GR_BLEND_ONE_MINUS_SRC_ALPHA;
            break;
         case GL_SRC_ALPHA_SATURATE:
         case GL_DST_COLOR:
         case GL_ONE_MINUS_DST_COLOR:
         case GL_DST_ALPHA:
         case GL_ONE_MINUS_DST_ALPHA:
            /* USELESS or TO DO */
            sfact=GR_BLEND_ZERO;
            break;
         default:
            break;
      }

      grAlphaBlendFunction(sfact,dfact,GR_BLEND_ONE,GR_BLEND_ZERO);

      grColorMask(FXTRUE,FXFALSE);
   } else {
      grAlphaBlendFunction(GR_BLEND_ONE,GR_BLEND_ZERO,GR_BLEND_ONE,GR_BLEND_ZERO);
      grColorMask(FXTRUE,FXFALSE);
   }

   if(ctx->Color.AlphaEnabled) {
      switch(ctx->Color.AlphaFunc) {
         case GL_NEVER:
            grAlphaTestFunction(GR_CMP_NEVER);
            break;
         case GL_LESS:
            grAlphaTestFunction(GR_CMP_LESS);
            break;
         case GL_EQUAL:
            grAlphaTestFunction(GR_CMP_EQUAL);
            break;
         case GL_LEQUAL:
            grAlphaTestFunction(GR_CMP_LEQUAL);
            break;
         case GL_GREATER:
            grAlphaTestFunction(GR_CMP_GREATER);
            break;
         case GL_NOTEQUAL:
            grAlphaTestFunction(GR_CMP_NOTEQUAL);
            break;
         case GL_GEQUAL:
            grAlphaTestFunction(GR_CMP_GEQUAL);
            break;
         case GL_ALWAYS:
            grAlphaTestFunction(GR_CMP_ALWAYS);
            break;
         default:
            break;
      }
      grAlphaTestReferenceValue(ctx->Color.AlphaRefUbyte);
   }
   else
      grAlphaTestFunction(GR_CMP_ALWAYS);

   if(ctx->Texture.Enabled) {
      switch(ctx->Texture.EnvMode) {
         case GL_DECAL:
            guColorCombineFunction(GR_COLORCOMBINE_DECAL_TEXTURE);
            guTexCombineFunction(GR_TMU0,GR_TEXTURECOMBINE_DECAL);
            break;
         case GL_MODULATE:
            if(ctx->Color.BlendEnabled)
               guAlphaSource(GR_ALPHASOURCE_TEXTURE_ALPHA);

            if(ctx->Light.ShadeModel==GL_SMOOTH)
               guColorCombineFunction(GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB);
            else
               guColorCombineFunction(GR_COLORCOMBINE_TEXTURE_TIMES_CCRGB);

            guTexCombineFunction(GR_TMU0,GR_TEXTURECOMBINE_DECAL);
            break;
         case GL_BLEND:
            /* TO DO */
            break;
         default:
            break;
      }
   }
   else {
      if(ctx->Light.ShadeModel==GL_SMOOTH)
         guColorCombineFunction(GR_COLORCOMBINE_ITRGB);
      else
         guColorCombineFunction(GR_COLORCOMBINE_CCRGB);
      
      grTexCombineFunction(GR_TMU0,GR_TEXTURECOMBINE_ZERO);
   }

   if(ctx->Depth.Test) {
      grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER); 

      switch(ctx->Depth.Func) {
         case GL_NEVER:
            grDepthBufferFunction(GR_CMP_NEVER);
            break;
         case GL_LESS:
            grDepthBufferFunction(GR_CMP_LESS);
            break;
         case GL_GEQUAL:
            grDepthBufferFunction(GR_CMP_GEQUAL);
            break;
         case GL_LEQUAL:
            grDepthBufferFunction(GR_CMP_LEQUAL);
            break;
         case GL_GREATER:
            grDepthBufferFunction(GR_CMP_GREATER);
            break;
         case GL_NOTEQUAL:
            grDepthBufferFunction(GR_CMP_NOTEQUAL);
            break;
         case GL_EQUAL:
            grDepthBufferFunction(GR_CMP_EQUAL);
            break;
         case GL_ALWAYS:
            grDepthBufferFunction(GR_CMP_ALWAYS);
            break;
         default:
            break;
      }

      if(ctx->Depth.Mask)
         grDepthMask(FXTRUE);
      else
         grDepthMask(FXFALSE);
   }
   else {
      grDepthBufferFunction(GR_CMP_ALWAYS);
      grDepthMask(FXFALSE);
   }

   if(ctx->Fog.Enabled && ctx->Hint.Fog==GL_NICEST) {
      grFogMode(GR_FOG_WITH_TABLE);
      grFogColorValue FXCOLOR((unsigned int)(255*ctx->Fog.Color[0]), 
                              (unsigned int)(255*ctx->Fog.Color[1]),
                              (unsigned int)(255*ctx->Fog.Color[2]), 
                              (unsigned int)(255*ctx->Fog.Color[3]));

      if((fxMesa->fogtablemode!=ctx->Fog.Mode) ||
         (fxMesa->fogdensity!=ctx->Fog.Density)) {
         float wscale = ((fxMesaContext)ctx->DriverCtx)->wscale;
         switch(ctx->Fog.Mode)	{
            case GL_LINEAR:
               guFogGenerateLinear(fxMesa->fogtable,
                                   ctx->Fog.Start / wscale,
                                   ctx->Fog.End / wscale);
               break;
            case GL_EXP:
               guFogGenerateExp(fxMesa->fogtable,ctx->Fog.Density*wscale);
               break;
            case GL_EXP2:
               guFogGenerateExp2(fxMesa->fogtable,ctx->Fog.Density*wscale);
               break;
            default: /* That should never happen */
               break; 
         }
         
         fxMesa->fogtablemode=ctx->Fog.Mode;
         fxMesa->fogdensity=ctx->Fog.Density;
      }
      
      grFogTable(fxMesa->fogtable);
   }
   else
      grFogMode(GR_FOG_DISABLE);
}


static const char *renderer_string(void)
{
   return "Glide";
}


static void setup_dd_pointers(GLcontext *ctx)
{
#if defined(DEBUG_FXMESA)
   printf("fxmesa: fx_mesa_setup_dd_pointers()\n");
#endif

   ctx->Driver.UpdateState = setup_dd_pointers;
	 
   ctx->Driver.RendererString = renderer_string;

   ctx->Driver.NearFar = SetNearFar;

   ctx->Driver.ClearIndex = NULL;
   ctx->Driver.ClearColor = clear_color;
   ctx->Driver.Clear = clear;

   ctx->Driver.Index = NULL;
   ctx->Driver.Color = set_color;

   ctx->Driver.SetBuffer = set_buffer;
   ctx->Driver.GetBufferSize = buffer_size;

   ctx->Driver.AllocDepthBuffer = alloc_depth_buffer;
   ctx->Driver.ClearDepthBuffer = clear_depth_buffer;

   /* acc. functions*/

   setup_fx_units(ctx);

   ctx->Driver.PointsFunc = choose_points_function(ctx);
   ctx->Driver.LineFunc = choose_line_function(ctx);
   ctx->Driver.TriangleFunc = choose_triangle_function(ctx);
   ctx->Driver.QuadFunc = choose_quad_function(ctx);
   ctx->Driver.RectFunc = NULL;

   ctx->Driver.Bitmap=drawbitmap;
   ctx->Driver.DrawPixels=NULL;

   ctx->Driver.RasterSetup = choose_setup_function(ctx);
   ctx->Driver.RenderVB = NULL;

   ctx->Driver.Finish=flush;
   ctx->Driver.Flush=finish;

   ctx->Driver.TexEnv=texenv;
   ctx->Driver.TexImage=teximg;
   ctx->Driver.TexParameter=texparam;
   ctx->Driver.BindTexture=texbind;
   ctx->Driver.DeleteTexture=texdel;

   ctx->Driver.WriteColorSpan       = NULL;
   ctx->Driver.WriteMonocolorSpan   = NULL;
   ctx->Driver.WriteColorPixels     = NULL;
   ctx->Driver.WriteMonocolorPixels = NULL;
   ctx->Driver.WriteIndexSpan       = NULL;
   ctx->Driver.WriteMonoindexSpan   = NULL;
   ctx->Driver.WriteIndexPixels     = NULL;
   ctx->Driver.WriteMonoindexPixels = NULL;

   ctx->Driver.ReadIndexSpan = NULL;
   ctx->Driver.ReadColorSpan = NULL;
   ctx->Driver.ReadIndexPixels = NULL;
   ctx->Driver.ReadColorPixels = NULL;
}


#define NUM_RESOLUTIONS 3

static resolutions[NUM_RESOLUTIONS][3] = { 
/*	{ 320, 200, GR_RESOLUTION_320x200 },*
	{ 320, 240, GR_RESOLUTION_320x240 },
	{ 400, 256, GR_RESOLUTION_400x256 },
	{ 512, 256, GR_RESOLUTION_512x256 },*/
	{ 512, 384, GR_RESOLUTION_512x384 },
/*	{ 640, 200, GR_RESOLUTION_640x200 },
	{ 640, 350, GR_RESOLUTION_640x350 },*/
	{ 640, 400, GR_RESOLUTION_640x400 },
	{ 640, 480, GR_RESOLUTION_640x480 } /*,
	{ 800, 600, GR_RESOLUTION_800x600 },
	{ 856, 480, GR_RESOLUTION_856x480 },
	{ 960, 720, GR_RESOLUTION_960x720 }*/ };

GrScreenResolution_t bestResolution(int width, int height)
{
   int i;

   for(i=0;i<NUM_RESOLUTIONS;i++)
      if((width<=resolutions[i][0]) && (height<=resolutions[i][1]))
         return resolutions[i][2];
	
   return GR_RESOLUTION_640x480;
}

fxMesaContext fxMesaCreateBestContext(GLuint win,GLint width, GLint height,
                                      const GLint attribList[])
{
   return fxMesaCreateContext(win, bestResolution(width,height),
                              GR_REFRESH_75Hz, attribList);
}

#ifdef __WIN32__
static int cleangraphics(void)
{
   fxMesaDestroyContext(CurrentfxMesaCtx);

   return 0;
}
#endif

/*
 * Create a new FX/Mesa context and return a handle to it.
 */
fxMesaContext fxMesaCreateContext(GLuint win,GrScreenResolution_t res,
                                  GrScreenRefresh_t ref,
                                  const GLint attribList[])
{
   fxMesaContext ctx;
   GrHwConfiguration hwconfig;
   int i;
   GLboolean doubleBuffer = GL_FALSE;
   GLboolean alphaBuffer = GL_FALSE;
   GLint depthSize = 0;
   GLint stencilSize = 0;
   GLint accumSize = 0;

   i = 0;
   while (attribList[i]!=FXMESA_NONE) {
      switch (attribList[i]) {
         case FXMESA_DOUBLEBUFFER:
            doubleBuffer = GL_TRUE;
            break;
         case FXMESA_ALPHA_SIZE:
            i++;
            alphaBuffer = attribList[i] > 0;
            break;
         case FXMESA_DEPTH_SIZE:
            i++;
            depthSize = attribList[i];
            break;
         case FXMESA_STENCIL_SIZE:
            i++;
            stencilSize = attribList[i];
            break;
         case FXMESA_ACCUM_SIZE:
            i++;
            accumSize = attribList[i];
            break;
         default:
            return NULL;
      }
      i++;
   }


#if defined(DEBUG_FXMESA)
   printf("fxmesa: fxMesaCreateContext()\n");
#endif

#ifndef FX_SILENT
   fprintf(stderr,"Mesa 3Dfx Voodoo Device Driver V0.17\nWritten by David Bucciarelli (tech.hmw@plus.it)\n");
#endif

   grGlideInit();
   if (grSstQueryHardware(&hwconfig)) {
#ifndef FX_SILENT
      char buf[80];
      grGlideGetVersion(buf);
#endif
      grSstSelect(0);

      if(hwconfig.SSTs[0].type==GR_SSTTYPE_VOODOO)
         win = 0;
#if USE_GLIDE_FULLSCREEN
      if(!grSstOpen(res,                  /* resolution */
                    ref,                  /* refresh */
                    GR_COLORFORMAT_ABGR,  /* pixel format */
                    GR_ORIGIN_LOWER_LEFT, /* origin */
                    GR_SMOOTHING_ENABLE,  /* smoothing mode */
                    2))                   /* num buffers */
 		    return NULL;
#else	/* Glide in a window */
      if(!grSstWinOpen((FxU32)win,res,ref,
                       GR_COLORFORMAT_ABGR,GR_ORIGIN_LOWER_LEFT,2,1))
         return NULL;
#endif

#ifndef FX_SILENT
      fprintf(stderr,"Using Glide V%s\nNumber of boards: %d\nGlide screen size: %dx%d\n",buf,hwconfig.num_sst,grSstScreenWidth(),grSstScreenHeight());
#endif
   }
   else {
      printf("ERROR: no Voodoo Graphics or Voodoo Rush !\n");
      return NULL;
   }

   ctx=(fxMesaContext)malloc(sizeof(struct fx_mesa_context));
   if(!ctx)
      return NULL;

   ctx->width=grSstScreenWidth();
   ctx->height=grSstScreenHeight();
   ctx->double_buffer = doubleBuffer;
   ctx->color=FXCOLOR(255,255,255,255);
   ctx->clearc=0;
   ctx->cleara=0;
   ctx->wscale=65535.0/100.0;
   ctx->fogdensity=-HUGE_VAL;
   ctx->currenttex=0;
   ctx->currentfb=GR_BUFFER_BACKBUFFER;

   grColorMask(FXTRUE,FXFALSE);
   if (doubleBuffer)
      grRenderBuffer(GR_BUFFER_BACKBUFFER);
   else
      grRenderBuffer(GR_BUFFER_FRONTBUFFER);


   for(i=0;i<MAXNUM_TEX;i++)
      ctx->texid[i]=GR_NULL_MIPMAP_HANDLE;

   ctx->gl_vis = gl_create_visual(GL_TRUE,     /* RGB mode */
                                  alphaBuffer,
                                  doubleBuffer,
                                  depthSize,   /* depth_size */
                                  stencilSize, /* stencil_size */
                                  accumSize,   /* accum_size */
                                  0,           /* index bits */
                                  255.0,       /* color scales */
                                  255.0,
                                  255.0,
                                  255.0,
                                  8, 8, 8, 0);

   ctx->gl_ctx = gl_create_context(ctx->gl_vis,
                                   NULL,  /* share list context */
                                   (void *) ctx);

   ctx->gl_buffer = gl_create_framebuffer( ctx->gl_vis );

   setup_dd_pointers(ctx->gl_ctx);
#ifdef __WIN32__
   onexit(cleangraphics);
#endif

   return ctx;
}


/*
 * Destroy the given FX/Mesa context.
 */
void fxMesaDestroyContext(fxMesaContext ctx)
{
#if defined(DEBUG_FXMESA)
   printf("fxmesa: fxMesaDestroyContext()\n");
#endif

   if(ctx) {
      gl_destroy_visual(ctx->gl_vis);
      gl_destroy_context(ctx->gl_ctx);
      gl_destroy_framebuffer(ctx->gl_buffer);

#ifndef FX_SILENT
      {
         GrSstPerfStats_t st;

         grSstPerfStats(&st);

         fprintf(stderr,"# pixels processed (minus buffer clears): %u\n",(unsigned)st.pixelsIn);
         fprintf(stderr,"# pixels not drawn due to chroma key test failure: %u\n",(unsigned)st.chromaFail);
         fprintf(stderr,"# pixels not drawn due to depth test failure: %u\n",(unsigned)st.zFuncFail);
         fprintf(stderr,"# pixels not drawn due to alpha test failure: %u\n",(unsigned)st.aFuncFail);
         fprintf(stderr,"# pixels drawn (including buffer clears and LFB writes): %u\n",(unsigned)st.pixelsOut);
         fprintf(stderr,"Free texture memory: %u bytes\n",(unsigned)guTexMemQueryAvail(GR_TMU0));
      }
#endif

      grGlideShutdown();

      free(ctx);
   }

   if(ctx==CurrentfxMesaCtx)
      CurrentfxMesaCtx=NULL;
}

/*
 * Make the specified FX/Mesa context the current one.
 */
void fxMesaMakeCurrent(fxMesaContext ctx)
{
#if defined(DEBUG_FXMESA)
   printf("fxmesa: fxMesaMakeCurrent()\n");
#endif

   if(!ctx) {
      gl_make_current(NULL,NULL);
      CurrentfxMesaCtx=NULL;

      return;
   }

   CurrentfxMesaCtx=ctx;

   gl_make_current(ctx->gl_ctx,ctx->gl_buffer);

   setup_dd_pointers(ctx->gl_ctx);   
   gl_Viewport(ctx->gl_ctx,0,0,ctx->width,ctx->height);
}


/*
 * Swap front/back buffers for current context if double buffered.
 */
void fxMesaSwapBuffers(void)
{
#if defined(DEBUG_FXMESA)
   printf("fxmesa: fxMesaSwapBuffers()\n");
#endif

   if (CurrentfxMesaCtx->double_buffer)
      grBufferSwap(1);
}

#else


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

int gl_fx_dummy_function(void)
{
   return 0;
}

#endif  /* FX */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.