/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmdraw2.c,v 1.20 2005/07/02 17:48:45 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.20 $
 * $Log: rmdraw2.c,v $
 * Revision 1.20  2005/07/02 17:48:45  wes
 * Updated rasterpos offset code for sprites to support 3D as well as 2D.
 * Some minor z-coordinate buglets remain for 3D sprite positioning - see
 * the comments in code for details.
 *
 * Revision 1.19  2005/06/26 18:50:11  wes
 * Numerous updates:
 * (1) Correct computation of viewport matrix, stuff into RMstate.
 * (2) Refine code that repositions the glRasterPosition for RM_SPRITE primitives;
 * 	works well in 2D, still needs a little help for 3D.
 * (3) Update RM_SPRITE draw code to take into account text justification parms.
 *
 * Revision 1.18  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.17  2005/05/28 16:25:57  wes
 * Fixed bug where the pickable quad associated with bitmap-based
 * primitives (RM_BITMAP, RM_TEXT, etc.) was incorrectly placed when
 * text justification attributes RM_CENTER or RM_RIGHT were in effect.
 *
 * Revision 1.16  2005/05/16 01:01:37  wes
 * Update some primitive draw routines to use private_rmSetGLTexCoordFunc
 * to obtain the correct OpenGL texture coordinate function.
 *
 * Revision 1.15  2005/05/15 15:20:42  wes
 * New internal routine to set the appropriate OpenGL texture coordinate function
 * as a function of RMprimitive TC data characteristics.
 *
 * Revision 1.14  2005/05/06 16:35:26  wes
 * Fixed buglet whereby prims specified with 0 verts would generate an
 * incomplete OpenGL display list, thereby causing a domino-effect
 * of errors.
 *
 * Revision 1.13  2005/03/16 16:43:17  wes
 * Fixed a text rendering bug introduced during code merge.
 *
 * Revision 1.12  2005/02/27 19:34:04  wes
 * Added support for application supplied texture object IDs and display lists.
 *
 * Revision 1.11  2005/02/19 16:23:56  wes
 * Distro sync and consolidation.
 * Added support for multitexturing.
 *
 * Revision 1.10  2005/02/12 00:54:50  wes
 * Preliminary multitexturing support for RMprimitives: qmesh, quads, tstrip,
 * triangles, and indexed triangles.
 *
 * Revision 1.9  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.8  2004/09/28 00:45:13  wes
 * Modified rmQuads to support per-face colors and/or normals. Moved
 * rmQuads from rmdraw1.c to rmdraw2.c
 *
 * Revision 1.7  2004/03/10 01:47:06  wes
 * Added rendering code for RM_INDEXED_QUAD_STRIP prim type.
 *
 * Revision 1.6  2004/02/23 03:04:32  wes
 * New primitives: RM_QUAD_STRIP, RM_INDEXED_TRIANGLES, RM_INDEXED_QUADS,
 * RM_INDEXED_TRIANGLE_STRIP.
 *
 * Revision 1.5  2004/01/16 16:44:05  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.4  2003/10/03 19:21:17  wes
 * Improved error checking and reporting in the rmText rendering function.
 *
 * Revision 1.3  2003/07/20 14:44:41  wes
 * Modified some declarations for RM support routines to support per-line
 * color in the disjoint lines primitives.
 *
 * Revision 1.2  2003/02/02 02:07:15  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.17  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.16  2002/12/02 16:24:56  wes
 * Bug fixes:
 * (1) fixed memory leak in rmPrimitiveSetText;
 * (2) fixed problem with textProps not being applied to 2nd and later
 * strings in an RM_TEXT text primitive.
 *
 * Revision 1.15  2002/11/14 15:34:51  wes
 * Minor editing for beautification.
 *
 * Revision 1.14  2002/08/29 22:20:32  wes
 *
 * Massive upgrade to accommodate dynamic object reallocation within
 * the component manager, and within the context cache. Use the
 * debug #define DEBUG_LEVEL DEBUG_REALLOC_TRACE to get a printf
 * whenever a realloc occurs. With this upgrade, there are no
 * OpenRM limits on the size of the scene graph. There will be external
 * limits, such as the amount of RAM and the amount of space available
 * to your OpenGL implementation.
 *
 * Revision 1.13  2002/06/17 00:57:01  wes
 * Fixed numerical problem related to calcuation of quad used when doing
 * GL_SELECT on bitmaps, sprites and text. Condensed the code used to
 * compute and draw those pickable quads; the new routines make use of
 * the internal routine private_rmNewWCfromDCOffset rather than compute
 * it separately. Added SHOW_PICKABLE_QUADS #define to draw the pickable
 * quads during normal rendering for visual reality check.
 *
 * Revision 1.12  2002/06/02 15:14:18  wes
 *
 * Added RMstateCache code to help eliminate the number of state changes
 * made during the render traversal. The RMstateCache tracks
 * the actual OpenGL rendering state w/o the need for querying OpenGL
 * directly, and  is queried by draw code that then decides if any
 * real state changes are required given the configuration of data
 * within an RMprimitive.
 *
 * Revision 1.11  2002/04/30 19:31:22  wes
 * Fixed a bug in the display of indexed text/bitmap primitives.
 *
 * Revision 1.10  2001/10/15 00:04:53  wes
 * Changed dispatching of characters in rmText from single char at a time
 * to string at a time.  The older code had some debug stuff in it that
 * checked the raster position after the glVertex2/3f() call, but before
 * the glBitmap() call. That calling sequence seems to cause problems for
 * Win32.
 *
 * Added an RMstate parm to the parm list for the setup funcs inside
 * draw routines for lines, text and images. This will cause lighting
 * to be enabled or disabled as a function of whether or not fogging
 * is active. It would be nice if fogging could be enabled, but lighting
 * disabled.
 *
 * Revision 1.9  2001/05/26 14:35:49  wes
 * Added error checking code to rmSprite().
 *
 * Revision 1.8  2001/03/31 17:12:38  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.7  2000/12/05 03:01:59  wes
 * Fixed problem with rmText in which default text properties were
 * malloc'ed, but never free'd.
 *
 * Revision 1.6  2000/12/03 22:34:37  wes
 * Mods for thread safety- RMpipe added as parm to display list
 * calls.
 *
 * Revision 1.5  2000/08/31 02:07:11  wes
 * No significant changes.
 *
 * Revision 1.4  2000/08/23 23:24:21  wes
 * DO_LISTS define moved from rmogl.c to rmprivat.h. All display
 * list code removed from rmBitmap (will be reinserted later).
 *
 * Revision 1.3  2000/08/19 14:58:29  wes
 * Culled unused code.
 *
 * Revision 1.2  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

#include <rm/rm.h>
#include "rmprivat.h"
#include "rmprivatps.h"

/*
 * 6/12/02 wes
 * for debugging purposes, you can set SHOW_PICKABLE_QUADS to a
 * non-zero value. this will cause the pickable quads associated with
 * rmText, rmBitmap and rmSprite primitives to be drawn *in addition to*
 * the source bitmap, text or sprite. usually, * these "helper" objects 
 * are not drawn except during selection mode.
 */
#define SHOW_PICKABLE_QUADS 0
/*#define SHOW_PICKABLE_QUADS 1   */


/*
 * this file contains RMprimitive "draw" routines for those
 * primitives that DO NOT easily map to OpenGL vertex arrays.
 */

extern void rmuCone(void (*colorfunc)(const float *), float *color, RMvertex3D *p1, RMvertex3D *p2, float radius1 ,int subdivisions, RMpipe *p);

/*
 * Begin glVertex* and pals macro/function layer
 * 
 * win-32 notes:
 *
 * un*x treats glVertex* and cousins as a normal function (pointer).
 * therefore, it is possible to access those functions through a pointer.
 * in MS VC, the glVertex* and pals have a verbose declaration which renders
 * indirect access (via a pointer) next to impossible.
 *
 * in order to remedy this situation, we use either macros (UNIX) or 
 * a subroutine layer (win-32) to temporarily work around this problem.
 * 
 */

#ifdef RM_WIN

void 
rmglVertex3fv (const GLfloat *v)
{
   glVertex3fv(v);
}

void 
rmglVertex2fv (const GLfloat *v)
{
   glVertex2fv(v);
}

void 
rmglNormal3fv (const GLfloat *v)
{
   glNormal3fv(v);
}

void 
rmglColor3fv (const GLfloat *a)
{
   glColor3fv(a);
} 

void
rmglColor4fv (const GLfloat *a)
{
   glColor4fv(a);
}

void 
rmglRasterPos2fv (const GLfloat *v)
{
   glRasterPos2fv(v);
}

void 
rmglRasterPos3fv (const GLfloat *v)
{
   glRasterPos3fv(v);
}

void
rmglTexCoord1fv (const GLfloat *v)
{
    glTexCoord1fv(v);
}

void
rmglTexCoord2fv (const GLfloat *v)
{
    glTexCoord2fv(v);
}

void
rmglTexCoord3fv (const GLfloat *v)
{
    glTexCoord3fv(v);
}

#endif

/* PRIVATE */
void
glNoOp (const float *f)
{
    /* do nothing */
    f = NULL; 			/* foil compiler warning */
}


/* PRIVATE */
void
private_rmSetGLColorFunc (int cvecsize,
			  int ncolors,
			  void (**cfuncptr)(const float *)) 
{
    /* set OpenGL color routine as a function of the  color vector length */
    if (ncolors == 0 || cvecsize == 0)
	*cfuncptr = glNoOp;
    else
    {
	if (cvecsize == 3)
	    *cfuncptr = rmglColor3fv;
	else
	{
	    if (cvecsize == 4)
		*cfuncptr = rmglColor4fv;
	    else
		*cfuncptr = glNoOp; /* warning? */
	}
    }
}

/* PRIVATE */
void
private_rmSetGLTexCoordFunc (int tcvecsize,
			     int ntc,
			     void (**tcfuncptr)(const float *)) 
{
    /* set OpenGL texture coord routine as a function of tc vector length */
    if (ntc == 0 || tcvecsize == 0)
	*tcfuncptr = glNoOp;
    else
    {
	switch(tcvecsize)
        {
	case 2:
	    *tcfuncptr = rmglTexCoord2fv;
	    break;
	case 3:
	    *tcfuncptr = rmglTexCoord3fv;
	    break;
	case 1:
	    *tcfuncptr = rmglTexCoord1fv;
	    break;
	default:
	    rmError("private_rmSetGLTexCoordFunc error: input tcveclen != 1,2, or 3 yet ntc != 0. Please file a bug report. ");
	    break;
	}
    }
}


/* PRIVATE */
void
private_rmStateGetTextJustify (const RMstate *s,
			       RMenum *hjustify,
			       RMenum *vjustify)
{
    extern RMenum RM_DEFAULT_HJUSTIFY, RM_DEFAULT_VJUSTIFY;
    
    if (s->textProps == NULL)
    {
	*hjustify = RM_DEFAULT_HJUSTIFY;
	*vjustify = RM_DEFAULT_VJUSTIFY;
    }
    else
    {
	*hjustify = s->textProps->hJustify;
	*vjustify = s->textProps->vJustify;
    }
}


/* precomputed trig tables */
#define TRIG_RES 360
double cos_table[TRIG_RES], sin_table[TRIG_RES];

/* PRIVATE */
void
private_initTrigTables (void)
{
    int    i;
    double t, dt;

    t = 0.0;
    dt = RM_TWO_PI / (double)TRIG_RES;
    
    for (i = 0; i < TRIG_RES; i++, t += dt)
    {
	cos_table[i] = cos(t);
	sin_table[i] = sin(t);
    }
}


/* PRIVATE */
static void
rmuCircle2d (int subdivisions)
{
    int   i;
    float t, dt;
    float v[2];

    /* draw a unit convex polygon at (0,0) */
    if (subdivisions > TRIG_RES)
	subdivisions = TRIG_RES;
    
    t = 0.0;
    dt = (float)(TRIG_RES) / (float)(subdivisions);

    glBegin(GL_POLYGON);
    for (i = 0; i < subdivisions; i++, t += dt)
    {
	v[0] = cos_table[(int)t];
	v[1] = sin_table[(int)t];
	glVertex2fv(v);
    }
    glEnd();
}


/* PRIVATE */
static void
rmuEllipse2d (int subdivisions,
	      float a,		/* vertical scale */
	      float b)		/* horizontal scale */
{
    int   i;
    float t, dt;
    float v[2];

    /* draw a unit convex polygon at (0,0) with the desired scaling */
    if (subdivisions > TRIG_RES)
	subdivisions = TRIG_RES;

    t = 0.0;
    dt = (float)(TRIG_RES) / (float)(subdivisions);

    glBegin(GL_POLYGON);
    for (i = 0; i < subdivisions; i++, t += dt)
    {
	v[0] = cos_table[(int)t] * b;
	v[1] = sin_table[(int)t] * a;
	glVertex2fv(v);
    }
    glEnd();
}

/* PRIVATE */
static void
private_rmSetupBitmapPickMatrices (RMstate *s,
				   RMmatrix *vpm,
				   RMmatrix *vpm_inv,
				   RMmatrix *forward,
				   RMmatrix *inv)
{
    RMmatrix m;

    rmMatrixIdentity(vpm);

    private_rmComputeViewportMatrix(s->vp, (float)s->w, (float)s->h, vpm);

    rmMatrixInverse(vpm, vpm_inv);

    rmMatrixMultiply(&(s->modelView), &(s->projection), &m); 
    *forward = m;
    
    rmMatrixInverse(&m, inv);
}

/* PRIVATE */
static void
private_rmDrawBitmapPickableQuads (RMmatrix *vpm,
				   RMmatrix *vpm_invOrig,
				   RMmatrix *forwardOrig,
				   RMmatrix *inv,
				   RMvertex3D *v,
				   int horigin,
				   int vorigin,
				   int bitmap_width,
				   int bitmap_height)
{
    RMvertex3D vnew[4];

    private_rmNewWCfromDCOffset(v, -horigin, -vorigin, vnew,
				vpm, vpm_invOrig, forwardOrig, inv);
    private_rmNewWCfromDCOffset(v, bitmap_width-horigin, -vorigin, vnew+1,
				vpm, vpm_invOrig, forwardOrig, inv);
    private_rmNewWCfromDCOffset(v, bitmap_width-horigin, bitmap_height-vorigin, vnew+2,
				vpm, vpm_invOrig, forwardOrig, inv);
    private_rmNewWCfromDCOffset(v, -horigin, bitmap_height-vorigin, vnew+3,
				vpm, vpm_invOrig, forwardOrig, inv);

    glBegin(GL_QUADS);
    glVertex3fv((float *)(vnew));
    glVertex3fv((float *)(vnew+1));
    glVertex3fv((float *)(vnew+2));
    glVertex3fv((float *)(vnew+3));
    glEnd();
}

/* PRIVATE
 * draw routine for the app-provided display list primitive
 */
void
rmAppDisplayList OGLPRIMPARMLIST()
{
    
#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    rmGLGetError("rmAppDisplayList() - before calling app display list");
#endif

    glCallList(p->flags1);

    /* foil compiler warning */
    r = NULL;
    s = NULL;
    renderPipe = NULL;
    rsc = NULL; 
    
#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    rmGLGetError("rmAppDisplayList() - after calling app display list");
#endif
}

/* PRIVATE
 *
 * draw routine for quadmesh routines
 */
void
rmQuadmesh OGLPRIMPARMLIST()
{
    float *nrow0=NULL, *nrow1=NULL;
    float *vrow0=NULL, *vrow1=NULL;
    float *crow0=NULL, *crow1=NULL;
    float *tcrow0 = NULL, *tcrow1 = NULL;
    int    usize, vsize;
    int   *dims;
    int    iv, iu;
    
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;
    int    listStat;
    
    /* vertex, normal, color, tc functions */
    void (*vertexfunc)(const float *);
    void (*normalfunc)(const float *);
    void (*colorfunc)(const float *);
    void (*tcfunc)(const float *);

    int haveMTCs = 0;

#if (DEBUG_LEVEL & DEBUG_GLSTATECHECK)
    private_glStateCheck(s);
#endif

    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compile warning */
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get texture coord data  */
    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    /* get the size info for the qmesh */
    private_rmGetBlobData(BLOB_QMESHDIMS_INDEX, p, NULL, NULL, (void *)&dims, NULL);
    
    if (p->multiTextureCoordBlobsMask != 0)
	haveMTCs = 1;

    vrow0 = v;
    nrow0 = n;
    crow0 = c;
    tcrow0 = tc;

    usize = dims[0];
    vsize = dims[1];
    
    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else
        vertexfunc = rmglVertex2fv;
    vrow1 = vrow0 + usize*vstride;
    
    if (nrow0 != NULL)
    {
	nrow1 = nrow0 + usize*nstride;
	normalfunc = rmglNormal3fv;
    }
    else
    {
	nrow0 = nrow1 = NULL;
	normalfunc = glNoOp;
    }

    if ((ncolors != usize*vsize) && (cstride != 0))
    {
	rmWarning(" only per-vertex colors are supported with the quadmesh. use quads as an alternative. drawing the quadmesh without colors.");
	cstride = 0;
	colorfunc = glNoOp;
    }
    else
    {
	private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);
	crow1 = crow0 + (usize * cstride);
    }
	
    if (ntc == 0)
	tcfunc = glNoOp;
    else
	private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);
	
    tcrow1 = tcrow0 + (usize * tcstride);
    
    for (iv = 0; iv < (vsize - 1); iv++) 
    {
	glBegin(GL_QUAD_STRIP);
	
	for (iu = 0; iu < usize; iu++)
        {
	    (*colorfunc)((float *)(crow1 + (iu * cstride)));
	    (*tcfunc)((float *)(tcrow1 + (iu * tcstride)));
	    (*normalfunc)((float *)(nrow1 + (iu * nstride)));
	    if (haveMTCs)
		private_dispatchMTCs(renderPipe, p, (iv+1) * usize + iu);

	    (*vertexfunc)((float *)(vrow1 + (iu * vstride)));
	    
	    (*colorfunc)((float *)(crow0 + (iu * cstride))); 
	    (*tcfunc)((float *)(tcrow0 + (iu * tcstride)));
	    (*normalfunc)((float *)(nrow0 + (iu * nstride)));

	    if (haveMTCs)
		private_dispatchMTCs(renderPipe, p, iv * usize + iu);

	    (*vertexfunc)((float *)(vrow0 + (iu * vstride)));
	}

	glEnd();

	vrow0 = vrow1;
	vrow1 += usize * vstride;

	nrow0 = nrow1;
	nrow1 += usize * nstride;

	crow0 = crow1;
	crow1 += usize * cstride;

	tcrow0 = tcrow1;
	tcrow1 += usize * tcstride;
    }
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmSpheres OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* radius data */
    float *radii = NULL;
    int    rstride, nradii, rveclen;

    int    i, listStat;
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);
    float  dummy_radius = 0.0;

    private_lightingStateManip(p, s, rsc, RM_TRUE);
    private_colorMaterialStateManip(p, s, rsc);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */

    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    
    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get radius data */
    private_rmGetBlobData(BLOB_SCALE_INDEX, p, &rstride, &nradii, (void **)&radii, &rveclen);

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else if (vveclen == 2)
        vertexfunc = rmglVertex2fv;
    
    if (nradii == 0)
    {
	radii = &dummy_radius;
	rstride = 0;
    }

    for (i = 0; i < nverts; i++, v += vstride, radii += rstride, c += cstride)
    {
	void rmuSphere(void (*colorfunc) (const float *), float *color, RMvertex3D *p1, float radius,  int model_switch, RMpipe *p);

	if (*radii != 0.0F)
	{
#if 0
	    /* tmp hack to test improved rendering when transparent.  */
	    /* this actually works surprisingly well..need to promote
	     this approach to a higher level. */

	    if (s->renderpass == RM_RENDERPASS_TRANSPARENT)
	    {
		glEnable(GL_CULL_FACE);
		glCullFace(GL_FRONT);
		rmuSphere(colorfunc, c, (RMvertex3D *)v, *radii,
			  rmPrimitiveGetModelFlag(p), renderPipe);
		glCullFace(GL_BACK);
		rmuSphere(colorfunc, c, (RMvertex3D *)v, *radii,
			  rmPrimitiveGetModelFlag(p), renderPipe);
		glDisable(GL_CULL_FACE);
	    }
	    else
#endif
	    rmuSphere(colorfunc, c, (RMvertex3D *)v, *radii,
		      rmPrimitiveGetModelFlag(p), renderPipe);
	}
	else
	{
	    /* we should be able to detect that the radius values
	       for the entire prim are null and to deal with them differently
	       (more efficiently) than doing them one-by-one here. */
	    
	    glBegin(GL_POINTS);
	    (*colorfunc)(c);
	    (*vertexfunc)(v);
	    glEnd();
	}
    }
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmCones OGLPRIMPARMLIST()
{
    /* for verts */
    int    vstride, nverts, vveclen;
    float *v;

    /* color data */
    int    cstride, ncolors, cveclen;
    float *c = NULL;

    /* radius data */
    int    rstride, nradii, rveclen;
    float *radii = NULL;

    /* cones */
    int    i, ncones, listStat;
    float  dummy_radius = 0.0;
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);

    private_lightingStateManip(p, s, rsc, RM_TRUE);
    private_colorMaterialStateManip(p, s, rsc);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL;			/* foil compiler warning */
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    
    ncones = nverts >> 1;	/* each vertex pair defines a cone (base,top) */
    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get radius data */
    private_rmGetBlobData(BLOB_SCALE_INDEX, p, &rstride, &nradii, (void **)&radii, &rveclen);
    
    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else
        vertexfunc = rmglVertex2fv;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (radii == NULL)
    {
        dummy_radius = 0.0;
	radii = &dummy_radius;
	rstride = 0;
    }

    for (i = 0; i < ncones; i++, v += 2 * vstride, c += cstride, radii += rstride)
    {
	RMvertex3D *p1, *p2;
	
	p1 = (RMvertex3D *)v;
	p2 = (RMvertex3D *)(v + vstride);

	rmuCone(colorfunc, c, p1, p2, *radii, rmPrimitiveGetModelFlag(p), renderPipe);
    }
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmCylinders OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* radius data */
    float *radii = NULL;
    int    rstride, nradii, rveclen;

    int    i, listStat, ncylinders;
    void (*colorfunc)(const float *);
    float  dummy_radius;

    private_lightingStateManip(p, s, rsc, RM_TRUE);
    private_colorMaterialStateManip(p, s, rsc);

    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    
    ncylinders = nverts >> 1;	/* each vertex pair defines a cone (base,top) */

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get radius data */
    private_rmGetBlobData(BLOB_SCALE_INDEX, p, &rstride, &nradii, (void **)&radii, &rveclen);

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    /* for now, assume 1 radius value per cylinder..so that they're
     straight (non-tapered) cylinders. also, we assume the layout of
     vertices is like that of disjoint lines..so that if we have
     n vertices, we'll render n/2 cylinders. */
    
    if (radii == NULL)
    {
        dummy_radius = 0.;
	radii = &dummy_radius;
	rstride = 0;
    }

    for (i = 0; i < ncylinders; i++, v += 2 * vstride, c += cstride, radii += rstride)
    {
	RMvertex3D *p1, *p2;
	
	void rmuCylinder(void (*colorfunc)(const float *), float *color, RMvertex3D *p1, RMvertex3D *p2,float radius1, int subdivisions, RMpipe *p);
	
	p1 = (RMvertex3D *)v;
	p2 = (RMvertex3D *)(v + vstride);

	rmuCylinder(colorfunc, c, p1, p2, *radii, rmPrimitiveGetModelFlag(p), renderPipe);
    }
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmMarkers2D OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* scale data */
    float *scales;
    int    sstride, nscales, sveclen;

    int    i, j;
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);

    RMinternalMarker2D *m;
    int                 nmarker_prims;
    RMmatrix            mx;

    float m00, m10, m01, m11, m30, m31;

    /*
     * 5/11/02
     * this code not yet tested - no demo programs that exercise 2d markers.
     */
    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);

    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    if (nverts == 0)
	return;

    r = NULL;  		/* foil compiler warning */
    renderPipe = NULL; 		/* foil compiler warning */
    
    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get radius data */
    private_rmGetBlobData(BLOB_SCALE_INDEX, p, &sstride, &nscales, (void **)&scales, &sveclen);

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    /* we could check to make sure only 2d vertices are permitted here... */
    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else
        vertexfunc = rmglVertex2fv;

    private_rmPrimitiveGetItem(p, RM_PRIMITIVE_MARKERS2D_PRIM, &nmarker_prims, (void **)&m);
    rmMatrixIdentity(&mx);

    /* need to use render state interface here. */
    {
	RMmatrix m;
	rmMatrixMultiply(&(s->modelView), &(s->projection), &m);
	rmMatrixInverse(&m, &m);

	mx.m[0][0] = 1. / m.m[1][1];
	mx.m[1][1] = s->aspect_ratio / m.m[0][0];
    }

    m00 = s->model.m[0][0];
    m10 = s->model.m[1][0];
    m01 = s->model.m[0][1];
    m11 = s->model.m[1][1];
    m30 = s->model.m[3][0];
    m31 = s->model.m[3][1];

    if (nmarker_prims != 1)
    {
	rmWarning("rmMarkers2D() error: at this time, only one markerprim per RMprimitive is permitted. This restriction will be removed in a future release. Drawing only the first one.");
    }
    
    /* temp - we assume that nmarker_prims == 1. */
    for (i = 0; i < nverts; i++, v += vstride, c += cstride, scales += sstride)
    {
        float *v2, v3[3];

        glPushMatrix();

	/* translate to the proper x/y location. this translation
	 accounts for scale, etc in the model-view-projection matrix.
	 the markers
	 will be positioned correctly, but will be rotation-invariant. */
	
	mx.m[3][0] = v[0] * m00 + v[1] * m10 + m30;
	mx.m[3][1] = v[0] * m01 + v[1] * m11 + m31;

	glMultMatrixf(&(mx.m[0][0])); 

	(*colorfunc)(c);

	v2 = (float *)(rmInternalMarker2DGetVerts(m));

	glBegin( rmInternalMarker2DGetBFlag(m) );
	
	for (j = 0; j < rmInternalMarker2DGetNpts(m); j++, v2 += 2)
        {
	    v3[0] = v2[0] * *scales ;
	    v3[1] = v2[1] * *scales ;
	    (*vertexfunc)(v3);
	}
	glEnd();
	
	glPopMatrix();
    }
}


/* unit cube geometry */
static RMvertex3D ibox[6][4] =
{
    {{1.0, 0.0, 0.0}, {1.0, 0.0, 1.0}, {1.0, 1.0, 1.0}, {1.0, 1.0, 0.0}}, /* +x face */
    {{0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 1.0}}, /* -x face */
    {{0.0, 1.0, 0.0}, {1.0, 1.0, 0.0}, {1.0, 1.0, 1.0}, {0.0, 1.0, 1.0}}, /* +y face */
    {{0.0, 0.0, 1.0}, {1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}, /* -y face */
    {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}}, /* front face */
    {{1.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, {0.0, 1.0, 1.0}, {1.0, 1.0, 1.0}}  /* back face */
};

/* unit cube normals */
static RMvertex3D iboxnorms[6] =
{
    {1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, -1.0}, {0.0, 0.0, 1.0}    
};


/* PRIVATE */
void
rmBox3d OGLPRIMPARMLIST() 
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    int         i, j, k, listStat;
    void      (*vertexfunc)(const float *), (*colorfunc)(const float *);
    RMvertex3D *rv;
    RMvertex3D  tbox[6][4];

    private_lightingStateManip(p, s, rsc, RM_TRUE);
    private_colorMaterialStateManip(p, s, rsc);
    private_textureStateManip(p, s, rsc);

    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);
    
    if (vveclen == 3)
	vertexfunc = rmglVertex3fv;
    else /* an error */
    {
	rmError("rmBox3D() error: box3d primitives MUST have 3d vertices. \n");
	return;
    }

    rv = (RMvertex3D *)v;

    for (i = 0; i < (nverts / 2); i++, rv += 2, c += cstride)
    {
	float sx, sy, sz;
	
	sx = rv[1].x - rv[0].x;
	sy = rv[1].y - rv[0].y;
	sz = rv[1].z - rv[0].z;

	for (k = 0; k < 6; k++)
	{
	    for (j = 0; j < 4; j++)
	    {
		tbox[k][j].x = ibox[k][j].x * sx;
		tbox[k][j].y = ibox[k][j].y * sy;
		tbox[k][j].z = ibox[k][j].z * sz;
	    }
	}

	glPushMatrix();
	glTranslatef(rv->x,rv->y,rv->z); /* won't work with 2d vertices */
	
	glBegin(GL_QUADS);
	(*colorfunc)(c); 
	for (j = 0; j < 6; j++) 
	{
/*	    glColor3fv((float *)&(debug_colors[j]));  */ /* debug */
	    glNormal3fv((float *)&(iboxnorms[j]));
	    for (k = 3; k >= 0; k--)
	    {
		(*vertexfunc)((float *)&(tbox[j][k]));
	    }
	}
	glEnd();

	glPopMatrix();
    }
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmBox3dWire OGLPRIMPARMLIST() 
{
    /* for verts */
    int    vstride, nverts, vveclen;
    float *v;

    /* color data */
    int    cstride, ncolors, cveclen;
    float *c = NULL;

    /* box */
    int         i, listStat;
    RMvertex3D *rv;
    void      (*vertexfunc)(const float *), (*colorfunc)(const float *);

    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    private_textureStateManip(p, s, rsc);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */

    /* vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmSetGLColorFunc(cveclen,ncolors, &colorfunc);
    
    if (vveclen == 3)
	vertexfunc = rmglVertex3fv;
    else /* an error */
    {
	rmError("rmBox3DWire() error: box3dwire primitives MUST have 3d vertices. \n");
	return;
    }

    rv = (RMvertex3D *)v;

    for (i = 0; i < (nverts / 2); i++, rv += 2, c += cstride)
    {
	float sx, sy, sz;
	
	sx = rv[1].x - rv[0].x;
	sy = rv[1].y - rv[0].y;
	sz = rv[1].z - rv[0].z;

	glPushMatrix();
	glTranslatef(rv->x, rv->y, rv->z); /* won't work with 2d vertices */

	glBegin(GL_LINE_STRIP);
	(*colorfunc)(c);
	
	glVertex3f(0.0F, 0.0F ,0.0F);
	glVertex3f(sx, 0.0F, 0.0F);
	glVertex3f(sx, sy, 0.0F);
	glVertex3f(0.0F, sy, 0.0F);

	glVertex3f(0.0F, 0.0F, 0.0F);
	glVertex3f(0.0F, 0.0F, sz);
	glVertex3f(0.0F, sy, sz);
	glVertex3f(0.0F, sy, 0.0F);
	
	glVertex3f(0.0F, sy, sz);
	glVertex3f(sx, sy, sz);
	glVertex3f(sx, sy, 0.0F);
	glVertex3f(sx, 0.0F, 0.0F);
	
	glVertex3f(sx, 0.0F, sz);
	glVertex3f(sx, sy, sz);
	glVertex3f(sx, 0.0F, sz);
	glVertex3f(0.0F, 0.0F, sz);
	
	glEnd();

	glPopMatrix();
    }
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmCircle2d OGLPRIMPARMLIST() 
{
    /*
     * TODO: 12/5/99
     * circles need to have display-list code added.
     *
     * ------ general info
     * for each of verts, color &  texture, we need
     * 1. stride, for memory accessing
     * 2. veclen, for proper gl function call
     *
     * the main delivery loop is over vertices. we check the number of
     * colors, norms & textures to ensure that we have either none or
     * per-vertex of each.
     */
    int    i;
    void (*colorfunc)(const float *);
    float  dummy = 1.0, scale;

    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;
   
    /* for radius data */
    float *radii= NULL;
    int    rstride, rveclen, nradii;
    
    RMmatrix m;

    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);  

    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get radius info */
    private_rmGetBlobData(BLOB_SCALE_INDEX, p, &rstride, &nradii, (void **)&radii, &rveclen);
    if (radii == NULL)
    {
	radii = &dummy;
	rstride = 0;
	nradii = 0;
    }

    /*
     * do validity checking:
     * 1. ncolors must be 0 or equal to nverts/2
     * 2. ntcoords must be 0 or equal to nverts/2
     */
    if (ncolors != 0 && ncolors != nverts)
    {
	rmError("error: rmCircle2D() only per-circle color is supported. \n");
	return;
    }

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    rmMatrixMultiply(&(s->model), &(s->projection), &m);
    rmMatrixMultiply(&m, &(s->vpm), &m);
    
    scale = RM_MIN(m.m[0][0], m.m[1][1]);

    for (i = 0; i < nverts; i++, v += vstride, c+= cstride, radii += rstride)
    {
	int      subdivisions = 9;
	RMmatrix m;
	
        glPushMatrix();
	rmMatrixIdentity(&m);

	m.m[0][0] = m.m[1][1] = m.m[2][2] = *radii;
	m.m[3][0] = v[0];
	m.m[3][1] = v[1];
	m.m[3][2] = 0.0F;
	glMultMatrixf((const GLfloat *)&(m.m[0][0]));
	(*colorfunc)(c);

	subdivisions = *radii * scale; /* this formulation results in line segments which are about 3 pixels long */

	    rmuCircle2d(subdivisions);
	
	glPopMatrix();
    }
    r = NULL; 			/* foil compiler warning */
    renderPipe = NULL; 		/* foil compiler warning */
}


/* PRIVATE
 *
 * because we use glRect() for drawing 2D boxes, the use of texture coords is not supported
 */
void
rmBox2d OGLPRIMPARMLIST() 
{
    /* for verts */
    int    vstride, nverts, vveclen;
    float *v, *v2;

    /* color data */
    int    cstride, ncolors, cveclen;
    int    i, nboxes, listStat;
    float *c = NULL;
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);

    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */
    
    /* vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);
 
    /*
     * do validity checking:
     * 1. ncolors must be 0 or equal to nverts/2
     */
    if ((ncolors != 0) && (ncolors != (nverts/2)))
    {
	rmError("error: rmBox2D() only per-box color is supported. \n");
	return;
    }

    if (vveclen == 3)		/* why check for 3D vertices on 2D boxes? */
        vertexfunc = rmglVertex3fv;
    else			/* assume vveclen == 2 */
        vertexfunc = rmglVertex2fv;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    nboxes = nverts / 2;
    v2 = v + vstride;
    
    for (i = 0; i < nboxes; i++, c += cstride)
    {
	(*colorfunc)(c);
	glRectfv((GLfloat *)(v), (GLfloat *)(v2));
	
	v = v2 += vstride;
	v2 += vstride;
    }
    
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}


/* PRIVATE */
void
rmEllipse2d OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* for radius data */
    float *radii = NULL;
    int    rstride, rveclen, nradii;

    /* for rotation data */
    float *rotdata;
    int    rotstride, rotveclen, nrots;
    
    int      i;
    float    scale;
    RMmatrix m;
    void   (*colorfunc)(const float *);
    float    dummy_rotate = 0.0;

    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get radius info */
    private_rmGetBlobData(BLOB_SCALE_INDEX, p, &rstride, &nradii, (void **)&radii, &rveclen);
    
    /* rotation data is stuffed into the blob otherwise used
       for indices. if we ever want to do indexed ellipses (?), then
       we'll have to use a different data blob. */

    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &rotstride, &nrots, (void **)&rotdata, &rotveclen);
    
    if (rotdata == NULL)
    {
	rotdata = &dummy_rotate;
	rotstride = 0;
    }

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);
    
    rmMatrixMultiply(&(s->model), &(s->projection), &m);
    rmMatrixMultiply(&m, &(s->vpm), &m);
    
    scale = RM_MIN(m.m[0][0], m.m[1][1]);

    /* careful on rveclen: we assume radius pairs w/o having to explicitly set veclen on the blob */
    for (i = 0; i < nverts; i++, v += vstride, c+= cstride, radii += 2, rotdata += rotstride)
    {
	RMmatrix m;
	int      subdivisions;
	float    rval;
	float    cval, sval;
	
        glPushMatrix();
	
	/* assume input in degrees */
	{
	    int idegrees;
	    /*
	     * input rotation value is expressed in terms of 10th's
	     * of degrees. our cos/sin table resolution is 1 degree, so
	     * we just throw away the additional resolution by dividing
	     * by 10.
	     */
	    idegrees = (int)(*rotdata * 0.10F);
	    idegrees = idegrees % 360;
	    idegrees = (idegrees < 0) ? idegrees + 360 : idegrees;
	    
	    cval = cos_table[idegrees];
	    sval = sin_table[idegrees];
	}

	rmMatrixIdentity(&m);

	m.m[0][0] = m.m[1][1] = cval;
	m.m[0][1] = sval;
	m.m[1][0] = -sval;

	m.m[3][0] = v[0];
	m.m[3][1] = v[1];
	m.m[3][2] = 0.0F;
	glMultMatrixf((const GLfloat *)&(m.m[0][0]));
	(*colorfunc)(c);

	rval = RM_MAX(radii[0], radii[1]);	/* radii must be defined */
	subdivisions = rval * scale; 		/* this formulation results in line segments which are about 3 pixels long */


	rmuEllipse2d(subdivisions, radii[0], radii[1]);

	
	glPopMatrix();
    }

    r = NULL; 			/* foil compiler warning */
    renderPipe = NULL; 		/* foil compiler warning */
}


/* PRIVATE */
void
rmSprite OGLPRIMPARMLIST()
{
    /*
     * display listing is handled a little differently for sprites &
     * bitmaps. the display list is associated with the RMimage object,
     * not the RMprimitive based upon the observation that the image
     * data itself is a higher-payload object than a vertex or two,
     * and to allow for this code to deal with weird raster position
     * placement issues that come up in OpenGL when drawing image
     * based data.
     */

    /* for verts */
    float    *v;
    int       vstride, nverts, vveclen;
    void    (*vertexfunc)(const float *);

    int       i;
    RMimage **sprite_list, *sprite;
    RMmatrix  vpm, vpm_inv, forward, inv;
    float     horigin = 0.0F, vorigin = 0.0F;
    GLenum    format, gltype;
    
    RMenum   hjustify, vjustify;

    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    private_lightingStateManip(p, s, rsc, RM_FALSE);

    if (vveclen == 2)
	vertexfunc = rmglRasterPos2fv;
    else /* assume vincr = 3 */
	vertexfunc = rmglRasterPos3fv;

    /* get justification settings specified for text */
    private_rmStateGetTextJustify(s, &hjustify, &vjustify);
    
    /*
     * now, grab the sprites. note that indexed sprites are not
     * currently supported.
     */
    private_rmPrimitiveGetItem(p, RM_PRIMITIVE_SPRITES, &i, (void **)&sprite_list);

    private_rmSetupBitmapPickMatrices(s, &vpm, &vpm_inv, &forward, &inv);

    for (i = 0; i < nverts; i++, v += vstride)
    {
	float xzoom, yzoom;
	
	sprite = sprite_list[i];

	if (sprite == NULL)
	    rmError("error in rmSprite() - NULL image pointer encountered in an RM_SPRITE primitive. \n");

	rmImageGetPixelZoom(sprite, &xzoom, &yzoom);
	
	horigin = 0.0F;
	if (hjustify == RM_CENTER)
	    horigin = (float)sprite->w * xzoom * 0.5F;
	else if (hjustify == RM_RIGHT)
	    horigin = (float)sprite->w * xzoom;
	
	vorigin = 0.0F;
	if (vjustify == RM_CENTER)
	    vorigin = (float)sprite->h * yzoom * 0.5F;
	else if (vjustify == RM_TOP)
	    vorigin = (float)sprite->h * yzoom;
	
	if (s->rendermode == GL_SELECT || SHOW_PICKABLE_QUADS)
        {
            /*
	     * in pick mode, we have to generate some pickable geometry
	     * since glDrawPixels doesn't generate anything we can pick.
	     * This hunk of code draws pickable geometry.
             */
	    int w, h;
	    RMvertex3D t;
	    
	    w = sprite->w * xzoom;
	    h = sprite->h * yzoom;

	    t.x = v[0];
	    t.y = v[1];
	    t.z = (vveclen == 2) ? 0.0F : v[2];
	    
            private_rmDrawBitmapPickableQuads(&vpm, &vpm_inv, &forward,&inv,
					      &t, horigin, vorigin, w, h);
        }
	
	if ((s->rendermode != GL_SELECT) || (SHOW_PICKABLE_QUADS))
        {
	    /*
	     * In rendering mode, compute the pixel location of the
	     * vertex value associated with the sprite. The final pixel
	     * coordinate will reflect the transformation of world
	     * coordinates to pixel coordinates as well as the offset
	     * resulting from the requested justification.
	     */
	    RMvertex3D dc;
	    RMvertex3D drawVertex;
	    RMvertex3D t;
	    float      xmove=0.0F, ymove=0.0F;
	    int        adjustRasterpos = 0;

	    t.x = v[0];
	    t.y = v[1];
	    t.z = (vveclen == 2) ? 0.0F : v[2];
	    
	    if (vveclen == 2)
		private_rmDCFromWC2(&t, &dc, 1, s);
	    else
		private_rmDCFromWC3(&t, &dc, 1, s);
		
	    dc.x -= horigin;
	    dc.y -= vorigin;

	    /* check to see if the requested vertex position is outside
	     the viewport. */

	    if ((dc.x <= 0.0F) || (dc.y <= 0.0F) || /* off to left or bottom */
		(dc.x >= s->w) || (dc.y >= s->h)) /* off to top or right */
	    {
		RMvertex3D newDC;
		float dx, dy;
		
		/* put the new vertex at the center of the screen */
		newDC.x = (float)(s->w) * 0.5;
		newDC.y = (float)(s->h) * 0.5;
		newDC.z = dc.z;

		/* what is the pixel delta from current vertex location in
		   pixel coordinates to pixel coordinates of proposed
		   new vertex?  */
		dx = newDC.x - dc.x;
		dy = newDC.y - dc.y;

		/*
		 * compute the new world coordinate. This approach produces
		 * the correct projected screen location, but the z-coordinate
		 * isn't exactly correct. If you have fog turned on, you may
		 * see a discontinous shade change when the sprite crosses
		 * over the left or bottom window boundary. 
		 */
		private_rmNewWCfromDCOffset((RMvertex3D *)v, dx, dy,
					    &drawVertex,
					    &vpm, &vpm_inv, &forward, &inv);
		xmove = -1.0F * dx;
		ymove = -1.0F * dy;

		adjustRasterpos = 1;
	    }
	    else
	    {
		/*
		 * the vertex with justification lies within the viewport.
		 * compute a new world coordinate for use in drawing
		 * the sprite.
		 */
		private_rmNewWCfromDCOffset(&t, -horigin, -vorigin,
					    &drawVertex,
					    &vpm, &vpm_inv, &forward, &inv);
		adjustRasterpos = 0;
	    }

	    
	    (*vertexfunc)((GLfloat *)&drawVertex);

	    /* warning!! generates a bitmap feedback token!! */
	    if (adjustRasterpos == 1)
		glBitmap(0, 0, 0, 0, (GLfloat)xmove, (GLfloat)ymove, NULL);

	    glPixelZoom ((GLfloat)xzoom, (GLfloat)yzoom);

	    format = private_rmImageGetOGLFormat(sprite);
	    gltype = private_rmImageGetOGLType(sprite);

	    private_glDrawPixels(private_rmImageGetWidth(sprite), private_rmImageGetHeight(sprite), format, gltype, (const GLvoid *)(private_rmImageGetPixelData(sprite)), sprite);
	    
	    /* all sprite/image display list stuff was ripped out
	     during the SBIR work. */
#if 0
	    /* try to build a display list for this image */
	    if (private_rmImageGetDirtyFlag(sprite) == RM_TRUE)
	    {
		private_rmImageSetDirtyFlag(sprite,RM_FALSE);
		if (sprite->d1 != -1)
		{
		    glDeleteLists(sprite->d1, 1);
		    sprite->d1 = -1;
		}
	    }

	    if (sprite->d1 == -1)
	    {

#if DO_LISTS
		sprite->d1 = glGenLists(1);

		glNewList(sprite->d1, GL_COMPILE_AND_EXECUTE);
#endif


		/* enable pixel transfer mode if there's a vismap present */
		private_glDrawPixels(private_rmImageGetWidth(sprite), private_rmImageGetHeight(sprite), format, gltype, (const GLvoid *)(private_rmImageGetPixelData(sprite)), sprite);



		/* 10/5/200 all RMimage display list stuff removed during
		 SBIR work.*/
#if DO_LISTS	    
		glEndList();
#endif

	    }
	    else
		glCallList(sprite->d1);
#endif	    
	}
    }
    r = NULL; 			/* foil compiler warning */
    renderPipe = NULL; 		/* foil compiler warning */
}


/* PRIVATE */
void
rmBitmap OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    int    i;

    void (*vertexfunc)(const float *);
    void (*colorfunc)(const float *);
    
    RMmatrix vpm, vpm_inv, forward, inv;
    RMenum   hjustify, vjustify;
    GLfloat  horigin, vorigin;
    int      nbitmaps;

    RMbitmap **bmp_list, *bmp;
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (vveclen == 2)
	vertexfunc = rmglRasterPos2fv;
    else /* assume vincr = 3 */
	vertexfunc = rmglRasterPos3fv;

    private_rmStateGetTextJustify(s, &hjustify, &vjustify);

    private_rmPrimitiveGetItem(p, RM_PRIMITIVE_BITMAPS, &nbitmaps, (void **)&bmp_list);

    if (nbitmaps != nverts)
    {
	rmError(" the number of bitmaps is not the same as the number of vertices in a bitmap primitive. \n");
	return;
    }

    if (s->rendermode == GL_SELECT || SHOW_PICKABLE_QUADS)       
    {
	/*
	 * in select mode, we're going to draw a bunch of quads rather
	 * than text bitmaps: bitmaps are not pickable, whereas quads
	 * are.  in order to do that, we need some matrices.
	 */
	private_rmSetupBitmapPickMatrices(s, &vpm, &vpm_inv, &forward, &inv);
    }

    for (i = 0; i < nverts; i++, v += vstride, c += cstride)
    {
	int bw, bh, bytes;
	
	bmp = bmp_list[i];
	rmBitmapGetSize(bmp, &bw, &bh, &bytes);
	
        if (c)
	    (*colorfunc)(c);

	horigin = 0.0F;
	if (hjustify == RM_CENTER)
	    horigin = (GLfloat)(bw >> 1);
	else 
	    if (hjustify == RM_RIGHT)
		horigin = (GLfloat)(bw);
	
	vorigin = 0.0F;

	if (vjustify == RM_CENTER)
	    vorigin = (GLfloat)(bh >> 1);
	else if (vjustify == RM_TOP)
	    vorigin = (GLfloat)(bh);
	
	if (s->rendermode == GL_SELECT || SHOW_PICKABLE_QUADS)       
	{
	    /* in select mode, draw quads using the matrices we computed earlier */
	   RMvertex3D t;
	   t.x = v[0];
	   t.y = v[1];
	   if (vveclen == 2)
	     t.z = 0.0F;
	   else
	     t.z = v[2];

 	   private_rmDrawBitmapPickableQuads(&vpm, &vpm_inv, &forward, &inv,
					     &t, horigin, vorigin, bw, bh);
	}
	if ((s->rendermode != GL_SELECT) || (SHOW_PICKABLE_QUADS))
	{
	    /* otherwise, we're in normal draw mode.  spit out the bitmaps */
	    (*colorfunc)((float *)c);
	    (*vertexfunc)((float *)v);
	    glBitmap((GLsizei)(bw), (GLsizei)(bh), horigin, vorigin, (GLfloat) 0.0F, (GLfloat)0.0F, (const GLubyte *)(private_rmBitmapGetPixelData(bmp)));
	}
    }

    r = NULL; 			/* foil compiler warning */
    renderPipe = NULL; 		/* foil compiler warning */
}


/* PRIVATE */
void
rmIndexedBitmap OGLPRIMPARMLIST()
{
    /*
     * todo: display list stuff.
     */
    rmError(" rmIndexedBitmap is broken. \n");

    /* foil compiler warning */
    r = NULL;
    p = NULL;
    s = NULL;
    renderPipe = NULL;
    rsc = NULL;
}


/* PRIVATE
 *
 * draw routine for indexed text prims
 */
void
rmIndexedText OGLPRIMPARMLIST()
{
    rmText(p, r, s, renderPipe, rsc);
}


/* PRIVATE
 *
 * draw routine for text prims
 */
void
rmText OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* index data */
    int   *indices, istride, nindices, iveclen;
    int    i, j;
    
    void (*vertexfunc)(const float *);
    void (*colorfunc)(const float *);

    RMenum hjustify, vjustify;
    int    do_indices;

    RMtextPrim *parray;	
    int          numtextprims;
    RMtextProps *tp;
    RMfontRegistry *fr;

    int usingTempTextProps = 0;

    RMmatrix     vpm, vpm_inv, forward, inv;

    private_colorMaterialStateManip(p, s, rsc);

    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get index data */
    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &istride, &nindices, (void **)&indices, &iveclen);
    
    if (indices != NULL)
	do_indices = 1;
    else
	do_indices = 0;
    
    if (vveclen == 3)
        vertexfunc = rmglRasterPos3fv;
    else
        vertexfunc = rmglRasterPos2fv;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (private_rmPrimitiveGetText(p, &numtextprims, &parray) == RM_WHACKED)
    {
       rmWarning(" rmText() rendering function error: the input primitive has no text strings defined. \n");
       return; /* 9/29/03 - need to verify that a simple return here is OK. */
    }

    /* loop to verify that we have proper width & height info
     using the current RMtextProps info. */
    tp = s->textProps;
    
    if (tp == NULL)
    {
	tp = private_rmDefaultTextProps(renderPipe);
	usingTempTextProps = 1;
    }

    /*
     * 10/2000 - thread-safety work:
     * 1. private_rmPrepareBitmapFont will conditionally build display
     *    lists for the English alphabet using the requested font and style
     * 2. get the rmFontRegistry handle for that font family on the
     *    named RMpipe - it contains display list indices used for
     *    drawing bitmaps.
     */
    private_rmPrepareBitmapFont(tp, renderPipe); /* build display lists */
    
    fr = private_rmFontRegistryEntry(tp->fontEnum, tp->sizeEnum, tp->italicEnum, tp->boldEnum, renderPipe->contextCache->pipeFontRegistry);

    /* assertion check on "fr" here? */
    
	    
    for (i = 0; i < numtextprims; i++)
    {

	/*
	 * 10/2000 TODO
	 * the following routine computes the character width
	 * and height of a character string when rendered given
	 * font family and style info. this info is needed for
	 * picking (selection) and for justification. in an
	 * earlier (non thread-safe) version of OpenRM, we cached
	 * some info in the RMtextPrim. we need to update this
	 * code so that we need not recompute character width
	 * and height during each frame rendering.
	 */
	int indx;

	if (do_indices == 1)
	    indx = indices[i];
	else
	    indx = i;
	
	private_rmTextPrimComputeCW(parray + indx, fr, renderPipe);
    }
    
    private_rmStateGetTextJustify(s, &hjustify, &vjustify);
    
    /* need w,h of the bitmaps in order to do justification & picking */
    if (s->rendermode == GL_SELECT || SHOW_PICKABLE_QUADS) 
    {
	private_rmSetupBitmapPickMatrices(s, &vpm, &vpm_inv, &forward, &inv);
    }
    
    for (i = 0, j = 0; i < nverts; i++, v += vstride, c += cstride)
    {
	int          bw, bh;
	int          horigin = 0, vorigin = 0;
	RMtextPrim *pt;

	if (do_indices == 1)
	    pt = parray + indices[i];
	else
	    pt = parray + i;
	bw = pt->bw;
	bh = pt->bh;

	horigin = 0.0F;
	if (hjustify == RM_CENTER)
	    horigin = (GLfloat)(bw >> 1);
	else 
	    if (hjustify == RM_RIGHT)
		horigin = (GLfloat)(bw);

	vorigin = 0.0F;
	if (vjustify == RM_CENTER)
	    vorigin = (GLfloat)(bh >> 1);
	else 
	    if (vjustify == RM_TOP)
		vorigin = (GLfloat)(bh);

	if (s->rendermode == GL_SELECT || SHOW_PICKABLE_QUADS)
	{
	    RMvertex3D tv;

	    tv.x = v[0];
	    tv.y = v[1];

	    /* 6/12/02 add code so that src z-coord is used if
	     present. this fixes a picking bug. */

	    if (vveclen == 2)
	      tv.z = 0.0; 
	    else
	      tv.z = v[2];

	    private_rmDrawBitmapPickableQuads(&vpm, &vpm_inv, &forward, &inv,
					      &tv, horigin, vorigin, bw, bh);

	} /* end selection mode */
	if ((s->rendermode == GL_RENDER) || SHOW_PICKABLE_QUADS)
	{
	    char *str;

	    (*colorfunc)(c);
	    (*vertexfunc)(v);

	    str = pt->string;
	    
	    {
		if (horigin != 0 || vorigin != 0)
		{
		    glBitmap(0, 0, 0, 0, -horigin, -vorigin, NULL);
		}
		
		glPushAttrib(GL_LIST_BIT);
		glListBase(fr->listbase - 32);

		glCallLists(strlen(str), GL_UNSIGNED_BYTE, str);
		glPopAttrib();
	    }
	}
	
	if (do_indices == 1)
	    j++;
    } /* loop over nverts */

    if (usingTempTextProps == 1)
	rmTextPropsDelete(tp);

    r = NULL; 			/* foil compiler warning */
}


/* PRIVATE */
void
rmIndexedTriangleFan OGLPRIMPARMLIST() 
{
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);
    void (*tcfunc)(const float *), (*normalfunc)(const float *);

    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;

    /* indices */
    int *indices;
    int  nindices, iveclen, istride;
    
    int i, listStat;

    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get indices */
    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &istride, &nindices, (void **)&indices, &iveclen);
    
    if ((nindices == 0) || (nverts == 0)) /* check for early termination */
	return;
    
    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else			/* assume vveclen == 2 */
        vertexfunc = rmglVertex2fv;

    if (n != NULL)		/* assume normals are in R3*/
	normalfunc = rmglNormal3fv;
    else
	normalfunc = glNoOp;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (ntc == 0)
	tcfunc = glNoOp;
    else
	private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);

    glBegin(GL_TRIANGLE_FAN);
    for (i = 0; i < nindices; i++)
    {
	int indx = indices[i];

	(*colorfunc)(c + (indx * cstride));
	(*normalfunc)(n + (indx * nstride));
	(*tcfunc)(tc + (indx * tcstride));
        (*vertexfunc)(v + (indx * vstride));
    }
    glEnd();
    
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}

/* PRIVATE */
void
rmIndexedQuads OGLPRIMPARMLIST() 
{
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);
    void (*tcfunc)(const float *), (*normalfunc)(const float *);

    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;

    /* indices */
    int *indices;
    int  nindices, iveclen, istride;
    
    int i, listStat;

    
    /* get indices */
    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &istride, &nindices, (void **)&indices, &iveclen);
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    if ((nindices == 0) || (nverts == 0)) /* check for early termination */
	return;
    
    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else			/* assume vveclen == 2 */
        vertexfunc = rmglVertex2fv;

    if (n != NULL)		/* assume normals are in R3*/
	normalfunc = rmglNormal3fv;
    else
	normalfunc = glNoOp;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (ntc == 0)
	tcfunc = glNoOp;
    else
	private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);

    glBegin(GL_QUADS);
    for (i = 0; i < nindices; i++)
    {
	int indx = indices[i];

	(*colorfunc)(c + (indx * cstride));
	(*normalfunc)(n + (indx * nstride));
	(*tcfunc)(tc + (indx * tcstride));
        (*vertexfunc)(v + (indx * vstride));
    }
    glEnd();
    
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}

/* PRIVATE */
void
rmIndexedTriangles OGLPRIMPARMLIST() 
{
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);
    void (*tcfunc)(const float *), (*normalfunc)(const float *);

    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;

    /* indices */
    int *indices;
    int  nindices, iveclen, istride;

    /* multitexturing */
    int haveMTCs = 0;
    
    int i, listStat;

    /* get indices */
    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &istride, &nindices, (void **)&indices, &iveclen);
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    if ((nverts == 0) || (nindices == 0)) /* check for early termination */
	return;

    r = NULL; 			/* foil compiler warning */
    
    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else			/* assume vveclen == 2 */
        vertexfunc = rmglVertex2fv;

    if (n != NULL)		/* assume normals are in R3 */
	normalfunc = rmglNormal3fv;
    else
	normalfunc = glNoOp;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (ntc == 0)
	tcfunc = glNoOp;
    else
	private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);

    if (p->multiTextureCoordBlobsMask != 0)
	haveMTCs = 1; 	

    glBegin(GL_TRIANGLES);
    for (i = 0; i < nindices; i++)
    {
	int indx = indices[i];

	(*colorfunc)(c + (indx * cstride));
	(*normalfunc)(n + (indx * nstride));
	(*tcfunc)(tc + (indx * tcstride));
	if (haveMTCs)
	    private_dispatchMTCs(renderPipe, p, indx);
        (*vertexfunc)(v + (indx * vstride));
    }
    glEnd();
    
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}

/* PRIVATE */
void
rmIndexedQuadStrip OGLPRIMPARMLIST() 
{
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);
    void (*tcfunc)(const float *), (*normalfunc)(const float *);

    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;

    /* indices */
    int *indices;
    int  nindices, iveclen, istride;
    
    int i, listStat;

    /* get indices */
    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &istride, &nindices, (void **)&indices, &iveclen);
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    if ((nverts == 0) || (nindices == 0)) /* check for early termination */
	return;
    
    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else			/* assume vveclen == 2 */
        vertexfunc = rmglVertex2fv;

    if (n != NULL)		/* assume normals are in R3 */
	normalfunc = rmglNormal3fv;
    else
	normalfunc = glNoOp;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (ntc == 0)
	tcfunc = glNoOp;
    else
	private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);

    glBegin(GL_QUAD_STRIP);
    for (i = 0; i < nindices; i++)
    {
	int indx = indices[i];

	(*colorfunc)(c + (indx * cstride));
	(*normalfunc)(n + (indx * nstride));
	(*tcfunc)(tc + (indx * tcstride));
        (*vertexfunc)(v + (indx * vstride));
    }
    glEnd();
    
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}

/* PRIVATE */
void
rmIndexedTriangleStrip OGLPRIMPARMLIST() 
{
    void (*vertexfunc)(const float *), (*colorfunc)(const float *);
    void (*tcfunc)(const float *), (*normalfunc)(const float *);

    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;

    /* indices */
    int *indices;
    int  nindices, iveclen, istride;
    
    int i, listStat;

    /* get indices */
    private_rmGetBlobData(BLOB_INDEX_INDEX, p, &istride, &nindices, (void **)&indices, &iveclen);
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);
    if ((nverts == 0) || (nindices == 0)) /* check for early termination */
	return;
    
    private_colorMaterialStateManip(p, s, rsc);
    private_lightingStateManip(p, s, rsc, RM_FALSE);
    
    listStat = private_rmPrimitiveDisplayListBegin(renderPipe, p);
    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    if (vveclen == 3)
        vertexfunc = rmglVertex3fv;
    else			/* assume vveclen == 2 */
        vertexfunc = rmglVertex2fv;

    if (n != NULL)		/* assume normals are in R3 */
	normalfunc = rmglNormal3fv;
    else
	normalfunc = glNoOp;

    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);

    if (ntc == 0)
	tcfunc = glNoOp;
    else
	private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);

    glBegin(GL_TRIANGLE_STRIP);
    for (i = 0; i < nindices; i++)
    {
	int indx = indices[i];

	(*colorfunc)(c + (indx * cstride));
	(*normalfunc)(n + (indx * nstride));
	(*tcfunc)(tc + (indx * tcstride));
        (*vertexfunc)(v + (indx * vstride));
    }
    glEnd();
    
    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}

/* PRIVATE 
 *
 * 8/14/04 - updated code to accommodate per-face colors, normals, etc.
 * When optional data items (colors, normals) are specified per-vertex,
 * then we use vertex arrays. Otherwise, we use a slower path through
 * OpenGL.
 */
void
rmQuads OGLPRIMPARMLIST()
{
    /* for verts */
    float *v;
    int    vstride, nverts, vveclen;

    /* color data */
    float *c = NULL;
    int    cstride, ncolors, cveclen;

    /* texture coord data */
    float *tc = NULL;
    int    tcstride;
    int    ntc, tcveclen;

    /* normals */
    float *n;
    int    nstride, nveclen, nnormals;

    int listStat;

    int perVertexColor=0, perFaceColor=0;
    int perVertexNormals=0, perFaceNormals=0;
    int useVertexArrays=1;

    int haveMTCs = 0;

    private_lightingStateManip(p,s, rsc, RM_FALSE); 
    private_colorMaterialStateManip(p,s, rsc);

    listStat = private_rmPrimitiveDisplayListBegin(renderPipe,p);

    if (listStat == 0)
	return;

    r = NULL; 			/* foil compiler warning */
    
    /* get vertex data */
    private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nverts, (void **)&v, &vveclen);

    /* get color data */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, &cstride, &ncolors, (void **)&c, &cveclen);

    /* get texture coord data - NOTE: (2/2000) tc's & quads not tested */
    private_rmGetBlobData(BLOB_TC_INDEX, p, &tcstride, &ntc, (void **)&tc, &tcveclen);

    /* get normals info */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, &nstride, &nnormals, (void **)&n, &nveclen);

    /*
     * check to see which code path to use
     */
    if (ncolors != 0)
    {
        if (ncolors == nverts)
	    perVertexColor = 1;
	else if ((ncolors << 2) == nverts)
	{
	    perFaceColor = 1;
	    useVertexArrays = 0;
	}
    }

    if (nnormals != 0)
    {
        if (nnormals == nverts)
	    perVertexNormals = 1;
	else if ((nnormals << 2) == nverts)
	{
	    perFaceNormals = 1;
	    useVertexArrays = 0;
	}
    }

    if (p->multiTextureCoordBlobsMask != 0)
    {
	useVertexArrays = 0; 	/* slow path until we figure out how to
				   use vertex arrays with MTCs. */
	haveMTCs = 1;
    }

    if (useVertexArrays == 1)
    {
        private_rmEnableVertexArrays(nverts, ncolors, nnormals, ntc, 0, 0);
    
	glVertexPointer(vveclen, GL_FLOAT, sizeof(float) * vstride, (const GLvoid *)v);
    
	if (ncolors != 0)
	    glColorPointer(cveclen, GL_FLOAT, sizeof(float) * cstride, (const GLvoid *)c);

	if (ntc != 0)
	    glTexCoordPointer(tcveclen, GL_FLOAT,sizeof(float) * tcstride, (const GLvoid *)tc);

	if (nnormals != 0)
	    glNormalPointer(GL_FLOAT, sizeof(float) * nstride, (const GLvoid *)n);
	
	glDrawArrays(GL_QUADS, 0, nverts);
    }
    else
    {
        /* vertex, normal, color, tc functions */
        void (*vertexfunc)(const float *);
	void (*normalfunc)(const float *);
	void (*colorfunc)(const float *);
	void (*tcfunc)(const float *);
	int i;

	if (vveclen == 3)
	    vertexfunc = rmglVertex3fv;
	else
	    vertexfunc = rmglVertex2fv;
    
	if (nnormals != 0)
	    normalfunc = rmglNormal3fv;
	else
	    normalfunc = glNoOp;

	if (ncolors != 0)
	    private_rmSetGLColorFunc(cveclen, ncolors, &colorfunc);
	else
 	    colorfunc = glNoOp;
	
	if (ntc == 0)
	    tcfunc = glNoOp;
	else
	    private_rmSetGLTexCoordFunc(tcveclen, ntc, &tcfunc);

	glBegin(GL_QUADS);

	for (i=0; i < nverts; i++)
	{
	    if (nnormals != 0)
	        (*normalfunc)(n), n += nstride;
	    if (ncolors != 0)
	        (*colorfunc)(c), c += cstride;
	    if (ntc != 0)
		(*tcfunc)(tc), tc += tcstride;
	    if (haveMTCs)
		private_dispatchMTCs(renderPipe, p, i);
	    
  	    (*vertexfunc)(v);
	    i++;
	    v += vstride;

	    if (nnormals != 0)
	        (*normalfunc)(n), n += nstride;
	    if (perVertexColor)
	        (*colorfunc)(c), c += cstride;
	    if (ntc != 0)
		(*tcfunc)(tc), tc += tcstride;
	    if (haveMTCs)
		private_dispatchMTCs(renderPipe, p, i);
	    
  	    (*vertexfunc)(v);
	    i++;
	    v += vstride;
	    
	    if (nnormals != 0)
	        (*normalfunc)(n), n += nstride;
	    if (perVertexColor)
	        (*colorfunc)(c), c += cstride;
	    if (ntc != 0)
		(*tcfunc)(tc), tc += tcstride;
	    if (haveMTCs)
		private_dispatchMTCs(renderPipe, p, i);
	    
  	    (*vertexfunc)(v);
	    i++;
	    v += vstride;
	    
	    if (nnormals != 0)
	        (*normalfunc)(n), n += nstride;
	    if (perVertexColor)
	        (*colorfunc)(c), c += cstride;
	    if (ntc != 0)
		(*tcfunc)(tc), tc += tcstride;
	    if (haveMTCs)
		private_dispatchMTCs(renderPipe, p, i);
	    
  	    (*vertexfunc)(v);
	    i++;
	    v += vstride;
	}

	glEnd();
    }


    private_rmPrimitiveDisplayListEnd(renderPipe, p, listStat);
}

/* PRIVATE */
/* 
 * dispatch 
 */
void
private_dispatchMTCs(RMpipe *pipe,
		     RMprimitive *prim, 
		     int vertexIndx)
{
    int i;
    int offset;
    int mask;
    RMprimitiveDataBlob *b;

    mask = prim->multiTextureCoordBlobsMask;
    for (i=0; (i < RM_MAX_MULTITEXTURES) && (mask != 0); i++)
    {
	if (mask & 0x1)
	{
	    GLint target = GL_TEXTURE0_ARB + i;
	    float *f;

	    b = prim->multiTextureCoordBlobs + i;
	    offset = private_rmBlobGetStride(b) / sizeof(float) * vertexIndx;
	    f = (float *)private_rmBlobGetData(b) + offset;

	    switch (private_rmBlobGetVeclen(b))
	    {
	    case 1:
		(*(pipe->caps->multiTexCoord1fvARB))(target, f);
		break;
	    case 2:
		(*(pipe->caps->multiTexCoord2fvARB))(target, f);
		break;
	    case 3:
		(*(pipe->caps->multiTexCoord3fvARB))(target, f);
		break;
	    default:
		printf(" private_dispatchMTS - blob vector length is not 1, 2 or 3. \n");
		break;
	    }
	}
	mask = mask >> 1;
    }
}

/* EOF */
