/*
 * 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: rmogl.c,v 1.14 2008/11/24 16:36:13 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.14 $
 * $Log: rmogl.c,v $
 * Revision 1.14  2008/11/24 16:36:13  wes
 * Couple of patches from Jorge
 *
 * Revision 1.13  2007/10/26 11:32:34  wes
 * Minor code streamlining in private_rmGLPopAttrib
 *
 * Revision 1.12  2005/06/26 19:00:12  wes
 * New routines for push/pop the OpenGL attrib stack. These contain lots
 * of error checking code that can be activated via DEBUG_LEVEL in rmprivat.h.
 *
 * Revision 1.11  2005/06/08 18:32:47  wes
 * Replaced APIENTRY with the well-defined GLAPIENTRY.
 *
 * Revision 1.10  2005/06/06 02:13:40  wes
 * Updated private_rmGLGetProcAddr to convert the function name argument
 * from type char * to GLbyte * (why use a ubyte for a function name??)
 *
 * Revision 1.9  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.8  2005/03/16 16:45:14  wes
 * Minor tweak to diagnostic output produced by my_glPopAttrib in debug mode.
 *
 * Revision 1.7  2005/02/19 16:28:54  wes
 * Distro sync and consolidation.
 * Fixes for a number of state tracking buglets.
 *
 * Revision 1.6  2005/01/23 17:08:25  wes
 * Copyright updated to 2005.
 * Updates to support access and use of extensions; multitexturing on all
 * platforms, use of 3d texture extension to realize implementation of
 * volume rendering and related functions on Windows platforms.
 *
 * Revision 1.5  2004/01/16 16:46:09  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.4  2003/03/16 21:56:16  wes
 * Documentation updates.
 *
 * Revision 1.3  2003/02/14 00:18:23  wes
 * Added new wrapper routine for glCallLists. In a CR implementation, it will
 * use the RMprimitive's bounding box as the source for parms for the
 * GL_OBJECT_BBOX_CR extension.
 *
 * 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.22  2003/01/27 05:04:42  wes
 * Changes to RMpipe API and initialization sequence to unify GLX, WGL and CR
 * platforms w/o too much disruption to existing apps.
 *
 * Revision 1.21  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.20  2003/01/11 18:44:22  wes
 * Added global control over whether or not display lists are used during
 * rendering by adding the new RMpipe controls rmPipeSetDisplayListEnable()
 * and rmPipeGetDisplayListEnable().
 *
 * Revision 1.19  2003/01/07 03:11:12  wes
 * Removed some debug printf's inside the display list dispatch routine.
 *
 * Revision 1.18  2002/12/31 00:55:22  wes
 *
 * Various enhancements to support Chromium - achitecture-specific sections
 * of RMpipe were cleaned up, etc.
 *
 * Revision 1.17  2002/11/27 00:44:19  wes
 * Added code that honors the rmPrimitive's display list enable flag.
 * If set to RM_FALSE, no display lists will be generated for a primitive.
 *
 * Revision 1.16  2002/09/05 15:07:08  wes
 * Inside delete texture code, we no longer check for glIsTexture
 * when a realloc of textureIDs is imminent. Saves a little time,
 * and avoids a segfault.
 *
 * Revision 1.15  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.14  2002/06/02 15:15:34  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.13  2002/04/30 19:32:47  wes
 * Updated copyright dates.
 *
 * Revision 1.12  2001/10/15 02:35:56  wes
 * Vectors, bitmaps and sprites will never be subject to lighting
 * when fogging is enabled. This was an error in the last checkin -
 * OpenGL fogging is not dependant upon lighting.
 *
 * Revision 1.11  2001/10/15 01:32:19  wes
 * Minor mods to support 4-byte alignment of framebuffer reads on Win32.
 * Problem showed up in demo "pdb."
 *
 * Revision 1.9  2001/07/15 17:19:19  wes
 * Added temporary code to routine that builds OpenGL display lists for
 * RMprimitive objects. The new code will remove an old OpenGL display
 * list before creating a new one.
 *
 * Revision 1.8  2001/06/03 20:48:45  wes
 * No significant differences.
 *
 * Revision 1.7  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.6  2000/12/03 22:33:55  wes
 * Mods for thread-safety.
 *
 * Revision 1.5  2000/10/03 11:40:50  wes
 * Contributions from jdb - prototype and compile warning cleanups.
 *
 * Revision 1.4  2000/08/31 02:11:06  wes
 * Tweaks to conditional compile code for local wrappers for
 * glPush/PopAttib.
 *
 * Revision 1.3  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.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.
 *
 */

/* documentation of public routines is incomplete in this file. */


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

/* PRIVATE */
RMenum
private_rmColorsEqual(RMcolor4D *c1,
		      RMcolor4D *c2)
{
#define CEPS 0.001
    float d1, d2, d3, d4;

    d1 = fabs(c1->r - c2->r);
    d2 = fabs(c1->g - c2->g);
    d3 = fabs(c1->b - c2->b);
    d4 = fabs(c1->a - c2->a);

    if ((d1 > CEPS) || (d2 > CEPS) || (d3 > CEPS) || (d4 > CEPS))
	return RM_FALSE;
    else
	return RM_TRUE;
}

/* PRIVATE */
void
private_rmSetBackBuffer (RMpipe *p)
{
    RMenum cf = rmPipeGetChannelFormat(p);

    if ((cf == RM_MONO_CHANNEL) ||
	(cf == RM_REDBLUE_STEREO_CHANNEL) ||
	(cf == RM_BLUERED_STEREO_CHANNEL) ||
	(cf == RM_MBUF_STEREO_CHANNEL))
	glDrawBuffer(GL_BACK);

    else
	glDrawBuffer(GL_FRONT);
}


/* PRIVATE */
void
private_rmReadBytePixels (unsigned char *pixelbuf,
			  int w,
			  int h,
			  int ncomponents,
			  GLenum pixel_component,
			  int bytesPerScanline)
{
    int j, offset = 0;
    int nbytes_per_component;
 
    /* the following line of code reads the entire framebuffer.  it's
       flipped in Y, so the loop below reads a row at a time. */ 
 
    nbytes_per_component = ncomponents * sizeof(unsigned char);

    offset = (h-1) * bytesPerScanline;

    for (j = 0; j < h; j++, offset -= (bytesPerScanline))
    {
	/* grab a row... */
	glReadPixels(0, j, w, 1, pixel_component, GL_UNSIGNED_BYTE, (pixelbuf + offset));
    }
}


/* PRIVATE  */
void
private_rmReadFloatPixels (float *pixelbuf,
			   int w,
			   int h,
			   int ncomponents,
			   GLenum pixel_component)
{
    int j, offset = 0;
 
    /* the following line of code reads the entire framebuffer.  it's
       flipped in Y, so the loop below reads a row at a time. */

    offset = (w * h * ncomponents) - (w * ncomponents);
    for (j=0; j < h; j++, offset -= (ncomponents * w))
    {
	/* grab a row... */
	glReadPixels(0, j, w, 1, pixel_component, GL_FLOAT, (pixelbuf + offset));
    }
}

/* PRIVATE */
int
private_rmGLPushAttrib (RMpipe *p, const RMnode *r, GLuint imask)
{
    /*
     * returns 0 on success, non-zero when there's an error
     */
    int rval = 0;
#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    /* debug */
    int ival;
    
    glGetIntegerv(GL_ATTRIB_STACK_DEPTH, &ival);
    rval = ival;
    
    if (r != NULL)
	fprintf(stderr, " before push attrib stack depth %d, <%s> \n", ival, r->object_info.name);
    else
	fprintf(stderr, " before push attrib stack depth %d <no node> \n", ival);
#endif
    
    glPushAttrib(imask);

    p->localMaskStack[p->localMaskStackTop] = imask;
    p->localMaskStackTop++;
    if (p->localMaskStackTop > MAX_MASK_STACK_DEPTH)
	rmError(" private_rmGLPushAttrib mask stack overflow! ");
    
#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    rval |= rmGLGetError("after glPushAttrib()");
#endif

    return rval;
}

/* PRIVATE */
int
private_rmGLPopAttrib (RMpipe *p, RMstate *s, RMstateCache *rsc)
{
    int rval = 0;
    
    glPopAttrib();

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    rval = glGetError();

    if (rval != 0)
    {
	char buf[128];

	sprintf(buf, "%s OpenGL error code (hex): 0x%04x ", s, rval);
	rmWarning(buf);
    }
#endif
    

    if (p->localMaskStack[p->localMaskStackTop] & (GL_ENABLE_BIT))
    {
	/* synchronize lighting state */
	rsc->lightingActive = s->lightingActive; /* ?? */
	rsc->colorMaterialActive = s->colorMaterialActive; /* ?? */

	/* todo: if rsc->colorMaterialActive is RM_TRUE, then the current
	 OpenGL color might not be the same as s->unlit_color. */
    }
    p->localMaskStackTop--;
    
    if (p->localMaskStackTop < 0)
    {
	rmError(" private_rmGLPushAttrib mask stack underflow! Please file a bug report.");
	p->localMaskStackTop = 0; /* to keep from blowing up */
    }
    
#if 0
    
    /* debug code used to coerce correct lighting state during
     state tracking debugging. Keeping it around for sentimental reasons */
    {
	int oglLightingEnabled;

	glFinish();

	oglLightingEnabled = glIsEnabled(GL_LIGHTING);
	
	if (oglLightingEnabled == GL_TRUE)
	{
	    rsc->lightingActive = s->lightingActive = RM_TRUE;
	}
	else
	{
	    rsc->lightingActive = s->lightingActive = RM_FALSE;
	}
    }
#endif

    /*
     * The following code doesn't always give reliable results: it
     * sometimes gives a false positive error indication.
     */
#if (DEBUG_LEVEL & DEBUG_GLSTATECHECK)
    {
	RMcolor4D c;
	int rD, gD, bD, aD;
	glGetFloatv(GL_CURRENT_COLOR, (GLfloat *)&c);

	rD = (int)((s->unlit_color.r - c.r)*255.0F);
	gD = (int)((s->unlit_color.g - c.g)*255.0F);
	bD = (int)((s->unlit_color.b - c.b)*255.0F);
	aD = (int)((s->unlit_color.a - c.a)*255.0F);

	if ((aD != 0) || (rD != 0) || (gD != 0) || (bD != 0))
	    rmWarning("private_rmGLPopAttrib - OpenGL current color != RMstate current color");
	
    }
#endif

    return rval;
}

/*
 * display list routines
 *
 * from inside each RMprimitive draw routine, we potentially call two
 * routines. the first one will either invoke a display list that's
 * already built, or will prepare to build one. at the end of the
 * RMprimitive draw routine, we call a routine that finalizes the
 * display list.
 */

/* PRIVATE  */
int
private_rmPrimitiveDisplayListBegin (RMpipe *pipe,
				     RMprimitive *p)
{
    /*
     * return values:
     * 0 = this routine invoked a display list, caller does not need
     *     to execute draw code
     * 1 = caller needs to execute draw code as part of th<
     *     display list build process. the caller must also call
     *     private_rmPrimitiveDisplayListEnd() to cause the display list
     *     to be finished.
     * 2 = caller needs to execute draw code, we're not building a
     *     display list
     * -1 = an error of some type.
     */
    
    int    stat = 2;
    GLuint newlist = 0;

    RMcacheKey primKey, contextCacheKey;
    int compListIndx;

    /*
     * check to see if display listing is disabled for this primitive.
     * if so, return "2" so that the caller will then invoke immediate
     * mode draw code.
     */
    if ((pipe->displayListEnableBool == RM_FALSE) ||
	(private_rmPrimitiveGetDisplayListEnable(p) == RM_FALSE))
	return stat;

#if DO_LISTS
    primKey = private_rmPrimitiveGetCacheKey(p);
    compListIndx = p->compListIndx;

    if (compListIndx >= pipe->contextCache->numPrimCacheKeys)
    {
	int newSize;
	int oldSize = pipe->contextCache->numPrimCacheKeys;
	int numNewPages = private_rmCacheComputeNumberNewPages(oldSize, NUM_ITEMS_PER_PAGE, compListIndx);
	int numOldPages = oldSize/NUM_ITEMS_PER_PAGE;

	newSize = numNewPages * NUM_ITEMS_PER_PAGE;
	
	pipe->contextCache->primCacheKeys = (RMcacheKey *)realloc(pipe->contextCache->primCacheKeys, newSize * sizeof(RMcacheKey));

	/* initialize new stuff to -1 */

	memset((void *)(pipe->contextCache->primCacheKeys + oldSize),
	       0xFF, (numNewPages-numOldPages)*NUM_ITEMS_PER_PAGE*sizeof(RMcacheKey));

	pipe->contextCache->numPrimCacheKeys = newSize;
	
#if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE)
	printf("private_rmPrimitiveDisplayListBegin() - reallocating contextCache->primCacheKeys; oldSize = %d, newSize = %d \n", oldSize, newSize);
#endif
    }

    contextCacheKey = pipe->contextCache->primCacheKeys[compListIndx];
    
    /*
     * compare primKey and contextCacheKey.
     * if they are equal, the OpenGL display list is up to date
     * wrt the contents of the RMprimitive. In this case, we can just
     * invoke the display list and return.
     *
     * if not, then first we check to see if the old display list
     * handle is a valid list, and if so, delete it. then, we begin
     * construction of an OpenGL display list.
     *
     */

    if (primKey == contextCacheKey) /* call the display list */
    {
	GLuint list;
	
	/* error check */
	if (compListIndx >= pipe->contextCache->numPrimDisplayListIDs)
	    /* this should never happen - the realloc should occur
	       before this branch of code is executed */
	    rmError("private_rmPrimitiveDisplayListBegin() error - the size of the primDisplayListIDs buffer is too small. \n");
	/** end debug code */
	
	list = pipe->contextCache->primDisplayListIDs[compListIndx];

#if 0
	fprintf(stderr," calling list %d \n", list);
	fflush(stderr);
#endif	
/*	glCallList(list); */
	
	private_glCallList(pipe, p, list);
	
	stat = 0;
    }
    else
    {
	/* error check */
	if (compListIndx >= pipe->contextCache->numPrimCacheKeys)
	    /* this should  never happen because we should have performed
	       any realloc before this chunk of code is executed. */
	    rmError("private_rmPrimitiveDisplayListBegin() error - the size of the primCacheKeys buffer is too small. \n");
	/** end debug code */
	
	pipe->contextCache->primCacheKeys[compListIndx] = primKey;

	/*
	 * 7/7/01 w.bethel
	 *
	 * by deleting the list here, we are inserting work in the
	 * critical path of rendering. this may not be a good idea
	 * in all circumstances.
	 *
	 * the way the component manager works, an rmPrimitiveDelete will
	 * move the newly deleted RMprimitive to the head of the alloc
	 * list, thereby increasing the liklihood that any old display
	 * list will be deleted the next time the RMprimitive is drawn.
	 * an alternate design strategy would be to construct a "to delete"
	 * list inside rmPrimitiveDelete which is later executed
	 * via an application-invoked synchronization function.
	 */
	
	if (compListIndx >= pipe->contextCache->numPrimDisplayListIDs)
	{
	    int newSize;
	    int oldSize = pipe->contextCache->numPrimDisplayListIDs;
	    int numNewPages = private_rmCacheComputeNumberNewPages(oldSize, NUM_ITEMS_PER_PAGE, compListIndx);
	    int numOldPages = oldSize/NUM_ITEMS_PER_PAGE;
	    
	    newSize = numNewPages * NUM_ITEMS_PER_PAGE;
	
	    pipe->contextCache->primDisplayListIDs = (GLuint *)realloc(pipe->contextCache->primDisplayListIDs, newSize * sizeof(GLuint));

	    /* initialize new stuff to -1 */

	    memset((void *)(pipe->contextCache->primDisplayListIDs + oldSize),
		   0xFF, (numNewPages-numOldPages)*NUM_ITEMS_PER_PAGE*sizeof(GLuint));

	    pipe->contextCache->numPrimDisplayListIDs = newSize;

#if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE)
	    printf("private_rmPrimitiveDisplayListBegin() - reallocating the primDisplayListIDs buffer, oldsize = %d, newSize = %d. \n", oldSize, newSize);
#endif
	}
	
	
	if (glIsList(pipe->contextCache->primDisplayListIDs[compListIndx]))
	    glDeleteLists(pipe->contextCache->primDisplayListIDs[compListIndx],1);
	
	pipe->contextCache->primDisplayListIDs[compListIndx] = newlist = glGenLists(1);
	if (newlist != 0)
	{
	    glNewList(newlist, GL_COMPILE);
	    stat = 1;
	}
	else
	    stat = -1;		/* error */
    }
#endif
    return(stat);
}


/* PRIVATE  */
int
private_rmPrimitiveDisplayListEnd (RMpipe *pipe,
				   RMprimitive *p,
				   int needFinalize)
{
    /*
     * when needFinalize != 0, we will assume that we need to
     * conclude construction of a display list.
     *
     * needFinalize might be non-zero when a primitive executes
     * it's own draw code rather than using a display list for
     * app-specific reasons, and we don't want to conclude construction
     * of a display list. 
     */
    if (needFinalize == 1)
    {
	GLuint list;
	int compListIndx = p->compListIndx;

	/* 8/29/02 debug code */
	if (compListIndx >= pipe->contextCache->numPrimDisplayListIDs)
	    /* this should never happen - we should have performed the
	       realloc before this point */
	    printf(" private_rmPrimitiveDisplayListEnd() error - the size of the primDisplayListIDs buffer is too small. compListIndx = %d, numPrimDisplayListIDs = %d.\n",compListIndx, pipe->contextCache->numPrimDisplayListIDs);
	
	list = pipe->contextCache->primDisplayListIDs[compListIndx];

	glEndList();
#if 0
	fprintf(stderr," calling list %d \n", list);
	fflush(stderr);
	glCallList(list); 
#endif	
	private_glCallList(pipe, p, list);
    }
    return(1);
}

/*
 * PRIVATE: delete an OpenGL texture from a specific OpenGL context
 */
void
private_rmOGLTextureDelete(RMtexture *toDelete,
			   RMpipe *p)
{
    int compListIndx;
    GLuint *id;
    
    compListIndx = toDelete->compListIndx;

    if (compListIndx >= p->contextCache->numTextureIDs)
    {
#if 0
	/*
	 * a realloc at this point is a quasi-error condition. "Quasi" because
	 * this code can be invoked on a texture that doesn't exist. This
	 * happens occasionally from private_rmManageTextureState() in
	 * rmframe.c.
	 */
	int newSize;
	int oldSize = p->contextCache->numTextureIDs;
	int numNewPages = private_rmCacheComputeNumberNewPages(oldSize, NUM_ITEMS_PER_PAGE, compListIndx);
	int numOldPages = oldSize/NUM_ITEMS_PER_PAGE;

	newSize = numNewPages * NUM_ITEMS_PER_PAGE;
	
	p->contextCache->textureIDs = (GLuint *)realloc(p->contextCache->textureIDs, newSize * sizeof(GLuint));
	
	/* initialize new stuff to -1 */
	memset((void *)(p->contextCache->textureIDs + oldSize),
	       0x00, (numNewPages-numOldPages)*NUM_ITEMS_PER_PAGE*sizeof(GLuint));

	p->contextCache->numTextureIDs = newSize;
	
#if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE)
	printf("private_rmOGLTextureDelete() - reallocating contextCache->textureIDs; oldSize = %d, newSize = %d \n", oldSize, newSize);
#endif

#endif
    }
    else
    {
	/* don't do a glIsTexture for something we know will obviously
	   not be loaded. */
	
	id = p->contextCache->textureIDs;
	if (glIsTexture(*(id+compListIndx)) == GL_TRUE)
	    glDeleteTextures(1, id+compListIndx);
	
	*(id+compListIndx) = 0;
    }
/*    *(id+compListIndx) = 0; */
}

void
private_lightingStateManip(RMprimitive *p,
			   RMstate *s,
			   RMstateCache *rsc,
			   RMenum forceNormals)
{
    /*
     * 5/11/02
     * lazy OpenGL state updates:
     * 1. if lighting is enabled and we don't want it, turn if off
     * 2. if lighting is off and we want it, turn it on.
     *
     * Assumption:  that *any* normals stored in the primitive implies
     * that lighting should be enabled; absence of normals implies
     * lighting should be disabled.
     *
     * control over each individual light source is controlled at the
     * RMnode level; we are controlling whether or not lighting is
     * active here, not the attribs of a specific light source.
     *
     * it is assumed we want lighting if:
     * the RMprimitive has normal data OR forceNormals==RM_TRUE
     *
     */

    int nnormals = 0;

    /*  it would be nice to find a faster way to check for the presence
     of normal data in the RMprimitive rather than have to poke around
     in the blob pile. the easiest way would be to set a bit in the
     RMprimitive when normals data is loaded up. */
    private_rmGetBlobData(BLOB_NORMAL_INDEX, p, NULL, &nnormals, NULL, NULL);

    if (forceNormals == RM_TRUE)
	nnormals++;		/* make it non-zero */
    
    /* if lighting is off and we want it, turn it on */
    if ((nnormals != 0) && (rsc->lightingActive == RM_FALSE))
    {
	glEnable(GL_LIGHTING);
	s->lightingActive = RM_TRUE;
	rsc->lightingActive = RM_TRUE;
    }
    
    /* if lighting is enabled and we don't want it, turn if off */
    if ( ((s->shademodel == RM_SHADER_NOLIGHT) || (nnormals == 0)) &&
	 (rsc->lightingActive == RM_TRUE) )
    {
	rsc->lightingActive = RM_FALSE;
	s->lightingActive = RM_FALSE;
	glDisable(GL_LIGHTING);
    }
    
#if (DEBUG_LEVEL & DEBUG_GLSTATECHECK)
    /*
     * debug code to read current lighting state from OpenGL and
     * compare it to the values in the state cache. You don't want
     * this test enabled in production code because it will slow you
     * down. Enable this code for debugging if you suspect lighting
     * state problems.
     */
    {
	int oglLightingEnabled;

	glFinish();

	oglLightingEnabled = glIsEnabled(GL_LIGHTING);

	if ((oglLightingEnabled == GL_TRUE) && (rsc->lightingActive != RM_TRUE))
	    rmError("private_lightingStateManip error: OpenGL lighting state != rm state");
	if ((oglLightingEnabled == GL_FALSE) && (rsc->lightingActive != RM_FALSE))
	    rmError("private_lightingStateManip error: OpenGL lighting state != rm state");
    }
#endif

}

void
private_colorMaterialStateManip(RMprimitive *p,
				RMstate *s,
				RMstateCache *rsc)
{
    /*
     * 5/14/02
     * lazy OpenGL state updates:
     * 1. if colormaterial is enabled and we don't want it, turn if off
     * 2. if colormaterial is off and we want it, turn it on.
     *
     * Assumption:  that *any* colors stored in the primitive requires
     * use of glColorMaterial. 
     */

    int ncolors;

    /*  it would be nice to find a faster way to check for the presence
     of color data in the RMprimitive rather than have to poke around
     in the blob pile. the easiest way would be to set a bit in the
     RMprimitive when color data is loaded up. */
    private_rmGetBlobData(BLOB_COLOR_INDEX, p, NULL, &ncolors, NULL, NULL);

#if (DEBUG_LEVEL & DEBUG_RSTATECACHECHECK)
    /* debug code */
    {
	int oglCMEnabled = glIsEnabled(GL_COLOR_MATERIAL);

	if ((oglCMEnabled == GL_TRUE) && (rsc->colorMaterialActive != RM_TRUE))
	    rmError("private_colorMaterialStateManipNew error: OpenGL colormaterial state ON, != rm state = OFF");
	if ((oglCMEnabled == GL_FALSE) && (rsc->colorMaterialActive != RM_FALSE))
	    rmError("private_colorMaterialStateManipNew error: OpenGL colormaterial state OFF != rm state ON");
    }
#endif

    /* if colormaterial is off and we want it, turn it on */
    if ((ncolors != 0) && (rsc->colorMaterialActive == RM_FALSE))
	private_rmColorMaterial(s, rsc, RM_TRUE);
    
    /* if colormaterial is enabled and we don't want it, turn if off */
    if (ncolors == 0)
    {
	if (rsc->colorMaterialActive == RM_TRUE)
	    private_rmColorMaterial(s, rsc, RM_FALSE);
    }
}

void
private_rmColorMaterial(RMstate *s,
			RMstateCache *rsc,
			RMenum newVal)
{
    if (newVal == RM_TRUE)
    {
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);
	rsc->colorMaterialActive = RM_TRUE;
    }
    else
    {
	rsc->colorMaterialActive = RM_FALSE;
	glColor4fv((GLfloat *)&(s->unlit_color));
	glDisable(GL_COLOR_MATERIAL);
    }
}

void
private_textureStateManip(RMprimitive *p,
			  RMstate *s,
			  RMstateCache *rsc)
{
    int ntc;
    private_rmGetBlobData(BLOB_TC_INDEX, p, NULL, &ntc, NULL, NULL);
    
    /* if lighting is off and we want it, turn it on */
    if ((ntc != 0) && (rsc->texturingActive == RM_FALSE))
    {
	glEnable(s->texture_mode);
	rsc->texturingActive = RM_TRUE;
    }
    
    /* if lighting is enabled and we don't want it, turn if off */
    if ( ((ntc == 0)) && (rsc->texturingActive == RM_TRUE) )
    {
	rsc->texturingActive = RM_FALSE;
	glDisable(s->texture_mode);
    }
}

/*
 * PRIVATE: private_glCallList(). this is a wrapper function for
 * glCallList. For CR applications, we issue a GL_OBJECT_BBOX_CR
 * call, then call the list, then turn off the GL_OBJECT_BBOX_CR.
 */
void
private_glCallList(RMpipe *pipe,
		   RMprimitive *prim,
		   GLuint listIndx)
{
#ifdef RM_CR
    RMvertex3D bmin, bmax;
    RMenum useBox;
    
    useBox = rmPrimitiveGetBoundingBox(prim,&bmin,&bmax);

    /* check for null crStuff - this can happen when the app doesn't call
       rmPipeNew() using RM_PIPE_CR, but OpenRM is built using -DRM_CR */
#if 1
    if ((pipe->crStuff != NULL) && (useBox == RM_CHILL))
    {
	float fvec[6];
	
	fvec[0] = bmin.x;
	fvec[1] = bmin.y;
	fvec[2] = bmin.z;
	fvec[3] = bmax.x;
	fvec[4] = bmax.y;
	fvec[5] = bmax.z;
	pipe->crStuff->glChromiumParametervCR(GL_OBJECT_BBOX_CR, GL_FLOAT, 6, (void *)fvec);
	
#if (DEBUG_LEVEL & DEBUG_CR_BBOX)
	fprintf(stderr, "CR_BBOX: PE %d setting GL_OBJECT_BBOX_CR to (%g, %g, %g) - (%g, %g, %g) \n", pipe->myRank, fvec[0], fvec[1], fvec[2], fvec[3], fvec[4], fvec[5]);
	fflush(stderr);
#endif
    }
#endif
    
#endif
    
    glCallList(listIndx);

#ifdef RM_CR
    if ((pipe->crStuff != NULL) && (useBox == RM_CHILL))
    {
	pipe->crStuff->glChromiumParametervCR(GL_DEFAULT_BBOX_CR, GL_FLOAT, 0, NULL);
#if (DEBUG_LEVEL & DEBUG_CR_BBOX)
	fprintf(stderr, "CR_BBOX: PE %d setting GL_DEFAULT_BBOX_CR \n", pipe->myRank);
	fflush(stderr);
#endif
    }
#else
    /* foil compiler warnings */
    pipe = NULL;
    prim = NULL;
#endif
}

void * 
private_rmGLGetProcAddr(const char *funcName)
{
#ifdef RM_X
    void (GLAPIENTRY *fptr)() = NULL;
    GLubyte *newFName = (GLubyte *)malloc(sizeof(GLubyte)*(strlen(funcName)+1));
    memcpy((void *)newFName, (void *)funcName, strlen(funcName));
    newFName[strlen(funcName)] = '\0';

    fptr = glXGetProcAddressARB(newFName);
    free(newFName); 		/* thanks, Jorge */
    if (fptr == NULL)
    {
	char buf[256];
	sprintf(buf, "private_rmGLGetProcAddress error: unable to find the routine named %s ", funcName);
	rmError(buf);
    }
    return fptr;
#endif
#ifdef RM_WIN
    PROC func;

    func = wglGetProcAddress(funcName);
    if (func == NULL)
    {
	char buf[256];
	sprintf(buf, "private_rmGLGetProcAddress error: unable to find the routine named %s ", funcName);
	rmError(buf);
    }

    return (void *)(func);
#endif
}

/* PRIVATE */
int
private_glStateCheck(RMstate *s)
{
    int rstat = 0; 		/* 0=OK, non-zero means error */
#if (DEBUG_LEVEL & DEBUG_GLSTATECHECK)
    /*
     * Compare the contents of an RMstate with the current OpenGL state
     * and report any differences. This is a debug routine that should
     * not be used in production because it will slow things way down.
     */

    /* shade model */
    {
	GLint oglShadeModel;
	glGetIntegerv(GL_SHADE_MODEL, &oglShadeModel);

	switch (oglShadeModel)
        {
	case GL_SMOOTH:
	    if (s->shademodel != RM_SHADER_SMOOTH)
	    {
		rmError("private_glStateCheck - OpenGL shader == GL_SMOOTH, RM shader != RM_SHADER_SMOOTH ");
		rstat = 1;
	    }
	    break;

	case GL_FLAT:
	    if ((s->shademodel != RM_SHADER_FLAT) && (s->shademodel != RM_SHADER_NOLIGHT))
	    {
		rmError("private_glStateCheck - OpenGL shader == GL_FLAT, RM shader != RM_SHADER_SMOOTH or RM_SHADER_NOLIGHT ");
		rstat = 1;
	    }
	    break;
	}
    }

    /* lighting */
    {
	GLint oglLightingEnabled;
	glGetIntegerv(GL_LIGHTING, &oglLightingEnabled);

	if ((oglLightingEnabled == 1) && (s->lightingActive != RM_TRUE))
	{
	    rmError("private_glStateCheck - OpenGL lighting is enabled, but RM lighting is not. ");
	    rstat = 1;
	}
	else if ((oglLightingEnabled == 0) && (s->lightingActive == RM_TRUE))
	{
	    rmError("private_glStateCheck - OpenGL lighting is NOT enabled, but RM lighting is enabled. ");
	    rstat = 1;
	}
    }

    /* color material */
    {
	GLint oglColorMaterialEnabled;
	glGetIntegerv(GL_COLOR_MATERIAL, &oglColorMaterialEnabled);

	if ((oglColorMaterialEnabled == 1) && (s->colorMaterialActive != RM_TRUE))
	{
	    rmError("private_glStateCheck - OpenGL COLOR_MATERIAL is enabled, but RM COLOR_MATERIAL is not. ");
	    rstat = 1;
	}
	else if ((oglColorMaterialEnabled == 0) && (s->colorMaterialActive == RM_TRUE))
	{
	    rmError("private_glStateCheck - OpenGL COLOR_MATERIAL is NOT enabled, but RM COLOR_MATERIAL is enabled. ");
	    rstat = 1;
	}
    }

    /* current color */
    {
	GLfloat c[4];
	RMcolor4D currentGLColor;
	
	glGetFloatv(GL_CURRENT_COLOR, c);
	currentGLColor.r = c[0];
	currentGLColor.g = c[1];
	currentGLColor.b = c[2];
	currentGLColor.a = c[3];

	if (private_rmColorsEqual(&currentGLColor, &(s->unlit_color)) == RM_FALSE)
	    rstat = 0;
    }


    if (rstat == 0)
	rmNotice("private_glStateCheck - OK. ");
    else
	rmNotice("private_glStateCheck - not OK. ");

#else
    s = NULL; 			/* foil compiler warning */
#endif

    return rstat;
}

/* EOF */
