/*
 * 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: rmmtdraw.c,v 1.12 2007/10/26 11:31:12 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.12 $
 * $Log: rmmtdraw.c,v $
 * Revision 1.12  2007/10/26 11:31:12  wes
 * Minor cleanup in conditional debug code
 *
 * Revision 1.11  2005/09/12 04:03:51  wes
 * Minor documentation updates.
 *
 * Revision 1.10  2005/06/26 18:53:48  wes
 * Consolidated opcode to capture both local state push/pop and OpenGL
 * attrib stack push/pop. This move helps streamline state tracking and
 * makes it possible to better track OpenGL state.
 *
 * 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:40:00  wes
 * Added more detailed information to be printed when DEBUG_LEVEL is
 * set to DEBUG_TRACE. Fixed a text attributes state manipulation bug
 * that was causing a stack underflow in OpenGL.
 *
 * Revision 1.7  2005/02/24 16:17:25  wes
 * Added support for fbClears to be set at the RMpipe level. Apps can
 * still set them at the RMnode level if desired for fine-grained control.
 *
 * Revision 1.6  2005/02/19 16:28:54  wes
 * Distro sync and consolidation.
 * Fixes for a number of state tracking buglets.
 *
 * Revision 1.5  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.4  2004/09/28 00:48:57  wes
 * Added render state cache as a parameter to routines that may modify
 * lighting state to fix a lighting state tracking problem.
 *
 * Revision 1.3  2004/01/16 16:46:09  wes
 * Updated copyright line for 2004.
 *
 * 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.13  2003/01/20 05:39:49  wes
 * Rewrote texture state handling code.
 *
 * Revision 1.12  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.11  2002/12/31 00:54:31  wes
 * Added check for post-rendering, pre-swapbuffers barrier function.
 * Added as part of Chromium work.
 *
 * Revision 1.10  2002/11/14 15:34:51  wes
 * Minor editing for beautification.
 *
 * Revision 1.9  2002/09/23 15:21:58  wes
 * Fixed a small memory leak inside the render traversal code.
 *
 * Revision 1.8  2002/09/17 14:13:49  wes
 * Removed code that tries to obtain an RMnode handle when a POP opcode
 * is encountered. The view traversal pushes a -1 index, which was making
 * the new component manager paging code ill. We don't need the node
 * handle anyway for pop operations.
 *
 * Revision 1.7  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.6  2002/08/17 15:11:14  wes
 * Added machinery to invoke user-defined pre- and post-traversal callbacks
 * in the render stage. At this time, the return value from the
 * pre-traversal callback is ignored. Effective early termination of
 * SG traversal with a pre-traversal callback should be done in the
 * view traversal.
 *
 * Revision 1.5  2002/06/30 21:41:31  wes
 * Added code that reinitializes the render state cache during multipass
 * stereo rendering. There was a lingering bug in which drawing that occurs
 * during the right channel was wrong because of a cache sync problem.
 *
 * Revision 1.4  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.3  2002/04/30 19:32:22  wes
 * Updated copyright dates.
 *
 * Revision 1.2  2001/06/03 20:47:03  wes
 * Removed unused vars to remove compile warnings.
 *
 * Revision 1.1  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 */

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

/*
 * table of functions used to process opcodes. need to have one
 * function per opcode
 */

void private_mtNoOp MTWORKPARMS();

void private_mtPushAttrib MTWORKPARMS();
void private_mtPopAttrib MTWORKPARMS();

void private_mtPushModelViewMatrix MTWORKPARMS();
void private_mtPopModelViewMatrix MTWORKPARMS();

void private_mtPushProjMatrix MTWORKPARMS();
void private_mtPopProjMatrix MTWORKPARMS();

void private_mtPushTextureMatrix MTWORKPARMS();
void private_mtPopTextureMatrix MTWORKPARMS();

void private_mtDraw MTWORKPARMS();

void private_mtFBclear MTWORKPARMS();

void private_mtPushTextProps MTWORKPARMS();
void private_mtPopTextProps MTWORKPARMS();

void private_mtPushState MTWORKPARMS();
void private_mtPopState MTWORKPARMS();

void private_mtPreTraverseCallback MTWORKPARMS();
void private_mtPostTraverseCallback MTWORKPARMS();

void private_mtPushAttribAndState MTWORKPARMS();
void private_mtPopAttribAndState MTWORKPARMS();

void (*mtRenderFuncs[]) MTWORKPARMS() =
{
    private_mtNoOp,
    private_mtPushAttrib,
    private_mtPopAttrib,
    private_mtPushModelViewMatrix, /* could be no-op */
    private_mtPopModelViewMatrix, /* could be no-op */
    private_mtPushProjMatrix,	/* could be no-op */
    private_mtPopProjMatrix,	/* could be no-op */
    private_mtPushTextureMatrix, /* could be no-op */
    private_mtPopTextureMatrix, /* could be no-op */
    private_mtDraw,
    private_mtFBclear,
    private_mtPushTextProps,
    private_mtPopTextProps,
    private_mtPushState,
    private_mtPopState,
    private_mtPreTraverseCallback,
    private_mtPostTraverseCallback,
    private_mtPushAttribAndState,
    private_mtPopAttribAndState
};

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
static void
myGlPushMatrix(GLenum matrixStack)
{
    int depth;
    if (matrixStack == GL_MODELVIEW)
    {
	glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth);
	fprintf(stderr," prior to ModelView push, depth=%d \n",depth);
    }
    else if (matrixStack == GL_PROJECTION)
    {
	glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &depth);
	fprintf(stderr," prior to Projection push, depth=%d \n",depth);
    }
    glPushMatrix();
}
#else
#define myGlPushMatrix(a) glPushMatrix()
#endif

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
static void
myGlPopMatrix(GLenum matrixStack)
{
    int depth;
    if (matrixStack == GL_MODELVIEW)
    {
	glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth);
	fprintf(stderr," prior to ModelView pop, depth=%d \n",depth);
    }
    else if (matrixStack == GL_PROJECTION)
    {
	glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &depth);
	fprintf(stderr," prior to Projection pop, depth=%d \n",depth);
    }
    glPopMatrix();
}
#else
#define myGlPopMatrix(a) glPopMatrix()
#endif

int
mtUpdateSceneParms (const RMnode *r,
		    RMstate *rState,
		    RMpipe *renderPipe,
		    RMstateCache *rsc)
{
    /*
     * this routine updates the scene parameters in rState to
     * reflect the changes requested by the input RMnode "r".
     *
     * cameras and viewports are processed prior to this routine.
     *
     * the rState parm is always updated, although not all scene
     * parameters cause a change to rState.
     *
     * when "applyGL" is set to RM_TRUE, the appropriate OpenGL
     * calls are made that have the scene parameters take effect.
     *
     * this routine returns a 1 if any changes were made to rState,
     * otherwise a 0 is returned. (June 3, 2001 - the return status
     * is not used by any consumers at this time - to avoid compile
     * warnings, we'll always return a 0 for now).
     *
     * the routines preCamFunc and perObjFunc are hooks used when
     * rendermode is GL_SELECT to drop bread crumbs so that cameras
     * generate feedback tokens, otherwise they are not used. 
     */
    
    /*
     * do background fill operations after the viewport is set.
     * note that we can't do BOTH background color AND a background image.
     *
     * don't draw tiles when we're in select mode.  although the spec
     * seems to indicate that no select events are generated for
     * drawpixels calls, i've had problems in mesa with them being
     * generated. (don't draw tiles in feedback mode. this is handled
     * as a modeling step when the PS file is created)
     */
    GLenum renderMode;

    renderMode = rState->rendermode;

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    {
	char buf[64];
	sprintf(buf, " mtUpdateSceneParms - before update ");
	rmGLGetError(buf);
    }
#endif
    if (r->scene_parms && r->scene_parms->textProps != NULL)
    {
	/*
	 * text props don't do anything to OpenGL.
	 * can we copy the pointer from the RMnode over rather
	 * than creating a new one?
	 * let's try doing that: 7/8/99
	 */
	rState->textProps = r->scene_parms->textProps;

#if 0
	/*
	 * a change of text props is otherwise significant (but not to
	 * OpenGL). if we've not already done a state push, we'll
	 * push something small on the stack.
	 *
	 * the issue is that a change in RMstate is bound to a change
	 * in OpenGL attribute stack depth
	 */
	if (push_attrib_enable != 1)
	{
	    if (applyGL == RM_TRUE)
		private_rmGLPushAttrib(r, GL_ACCUM_BUFFER_BIT); /* something small */

	    push_attrib_enable = 1;
	}
#endif
    }

    /* viewport in state needed for pick matrix */
    if (r->scene_parms && r->scene_parms->viewport)	/* do viewport */
	private_setViewport(r, rState, RM_FALSE, RM_TRUE);

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    {
	char buf[64];
	sprintf(buf, " mtUpdateSceneParms - after viewport ");
	rmGLGetError(buf);
    }
#endif
    
    /* do the lights */
    process_scene_lights(r, 1, rState, RM_TRUE, rsc);

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    {
	char buf[64];
	sprintf(buf, " mtUpdateSceneParms - after light ");
	rmGLGetError(buf);
    }
#endif
    /* do clipping planes if present */
    private_setClipPlanes(r, 1, rState, RM_TRUE);

    /* do fog if present */
    private_setFog(r, 1, rState, RM_TRUE);
    
    /* do texture if present */
    if ((r->scene_parms != NULL) && (r->scene_parms->haveAnyTextures == RM_TRUE))
    {
	/*	RMtexture *t = r->scene_parms->texture; */
	int i;

	for (i=0; i <= RM_MAX_MULTITEXTURES; i++)
	    if (r->scene_parms->textures[i] != NULL)
		private_manageTextureState(r->scene_parms->textures[i],rState,renderPipe, 1, i);
    }

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    {
	char buf[64];
	sprintf(buf, " mtUpdateSceneParms - after texture update ");
	rmGLGetError(buf);
    }
#endif
    
    return 0;
}

void
mtUpdateSurfaceProps(const RMnode *r,
		     RMstate *rState)
{
    /* assume r->sprops is != NULL */
    /* update surface properties if present */
    private_setSurfaceProps(r, 1, rState, RM_TRUE);
    
    /* 8/8/04 - move from mtUpdateRenderProps to here because the
       unlit_color attrib is set in private_setSurfaceProps. */
    glColor4fv((GLfloat *)&(rState->unlit_color));
}

void
mtUpdateRenderProps(const RMnode *r,
		    RMstate *rState,
		    RMstateCache *rsc)
{
    /* assume r->rprops != NULL */
    /* update rendering properties if present */

    /* 6/2/05 - why are we not passing rsc to private_setRenderProps? */
    /*    private_setRenderProps(r, 1, rState, RM_TRUE, NULL); */
    
    private_setRenderProps(r, 1, rState, RM_TRUE, rsc); 
}
    

void
private_mtNoOp MTWORKPARMS()
{
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    rsc = NULL;
    indx = 0;
}

void
private_mtPushAttribAndState MTWORKPARMS()
{
    GLuint mask = private_rmNodeGetAttribMask(n);

    if (mask != 0)
	private_rmGLPushAttrib(p, n, mask); 

    if (n->scene_parms != NULL)
	mtUpdateSceneParms(n, s, p, rsc);

    if (n->rprops != NULL)
	mtUpdateRenderProps(n, s, rsc);
    
    if (n->sprops != NULL)
	mtUpdateSurfaceProps(n, s);

    /* is it enough to do this just here? can we avoid this call in some cases?*/
    private_rmStateCacheSync(s, rsc);
    
    /* foil compiler warnings */
    indx = 0;
}

void
private_mtPopAttribAndState MTWORKPARMS()
{
    private_rmGLPopAttrib(p, s, rsc);
    private_rmStateCacheSync(s, rsc);  
    
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    indx = 0;
}

void
private_mtPushAttrib MTWORKPARMS()
{
    GLuint mask = private_rmNodeGetAttribMask(n);

    if (mask != 0)
	private_rmGLPushAttrib(p, n, mask); 

    if (n->scene_parms != NULL)
	mtUpdateSceneParms(n, s, p, rsc);

    if (n->rprops != NULL)
	mtUpdateRenderProps(n, s, rsc);
    
    if (n->sprops != NULL)
	mtUpdateSurfaceProps(n, s);

    /* is it enough to do this just here? can we avoid this call in some cases?*/
    private_rmStateCacheSync(s, rsc);
    
    /* foil compiler warnings */
    indx = 0;
}

void
private_mtPushState MTWORKPARMS()
{
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    rsc = NULL;
    indx = 0;
}

void
private_mtPopState MTWORKPARMS()
{
    /*
     * synchronize the RMstateCache with the working RMstate
     */
    /*    private_rmStateCacheSync(s, rsc); */
    private_rmSyncStateToCache(rsc, s);
    
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    indx = 0;
}

void
private_mtPostTraverseCallback MTWORKPARMS()
{
    int (*afunc)(const RMnode *, const RMstate *);
    int rstat;
    
    if (n->renderPosttraverseCallback == NULL)
    {
	rmError(" private_mtPostTraverseCallback() inconsistency check fails: a node's render-stage post traversal callback was scheduled for invocation by view for render, but the func callback is NULL in the node!! Cannot invoke the post-traversal callback during the render stage.");
	return;
    }
    
    afunc = n->renderPosttraverseCallback;
    rstat = (*afunc)((const RMnode *)n, (const RMstate *)s);
    
    /* foil compiler warnings */
    p = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPreTraverseCallback MTWORKPARMS()
{
    int (*afunc)(const RMnode *, const RMstate *);
    int rstat;
    
    if (n->renderPretraverseCallback == NULL)
    {
	rmError(" private_mtPreTraverseCallback() inconsistency check fails: a node's render-stage post traversal callback was scheduled for invocation by view for render, but the func callback is NULL in the node!! Cannot invoke the post-traversal callback during the render stage.");
	return;
    }
    
    afunc = n->renderPretraverseCallback;
    rstat = (*afunc)((const RMnode *)n, (const RMstate *)s);
    
    /* foil compiler warnings */
    p = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPushTextProps MTWORKPARMS()
{
    /* update state with new textprops */
    /* tmp */
    s->textProps = n->scene_parms->textProps;
    
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPopTextProps MTWORKPARMS()
{
    /* reinstate text props */
#if 0
    /* 3/16/05 - how to reinstate RMstate-level textprops? Using
     gl's push/pop attrib is not the right way to go. The following
    line of code was removed 3/16/05 due to stack underflow errors. */
    my_glPopAttrib(); 		/* 8/8/04 wes tmp test */
#endif
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void private_mtPopAttrib MTWORKPARMS()
{
    private_rmGLPopAttrib(p, s, rsc);
    private_rmStateCacheSync(s, rsc); 
    
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    indx = 0;
}

void
private_mtPushModelViewMatrix MTWORKPARMS()
{
#if 0
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadMatrixf(&(s->modelView.m[0][0]));
#endif
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPopModelViewMatrix MTWORKPARMS()
{
    glMatrixMode(GL_MODELVIEW);
    myGlPopMatrix(GL_MODELVIEW);
    
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPushProjMatrix MTWORKPARMS()
{
#if 0
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadMatrixf(&(s->projection.m[0][0]));
#endif
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPopProjMatrix MTWORKPARMS()
{
    glMatrixMode(GL_PROJECTION);
    myGlPopMatrix(GL_PROJECTION);
    
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPushTextureMatrix MTWORKPARMS()
{
#if 0
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadMatrixf(&(s->textureMatrix.m[0][0]));
#endif
    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtPopTextureMatrix MTWORKPARMS()
{
    glMatrixMode(GL_TEXTURE);
    glPopMatrix();

    /* foil compiler warnings */
    p = NULL;
    n = NULL;
    s = NULL;
    indx = 0;
    rsc = NULL;
}

void
private_mtDraw MTWORKPARMS()
{
    RMprimitive *prim;
    int i, nprims;

    nprims = rmNodeGetNumPrims(n);
    for (i=0;i<nprims;i++)
    {
	void (*renderfunc)  OGLPRIMPARMLIST() ;
	prim = n->prims[i];

	if (p != NULL)
	{
	    renderfunc = prim->renderfunc;
	    (*renderfunc)(prim, n, s, p, rsc);
	}
    }
    
    /* foil compiler warnings */
    indx = 0;
}

void
private_mtFBclear MTWORKPARMS()
{
    GLint renderMode = s->rendermode;
    int backgroundSceneEnable = 1; /* tmp hack */

    if ((n->fbClear && n->fbClear->bgImageTile) && (backgroundSceneEnable)
	&& (renderMode != GL_SELECT) && (renderMode != GL_FEEDBACK))
    {
	private_setBackgroundTile(n->fbClear, s, 1, RM_TRUE);
    }
    else if ((n->fbClear && n->fbClear->bgColor) && (backgroundSceneEnable))
    {
	private_setBackgroundColor(n->fbClear, s, 1, RM_TRUE);
    }

    /* check depth value or image parms */
    if (n->fbClear && (n->fbClear->depthValue != NULL) && (backgroundSceneEnable))
	private_setBackgroundDepthValue(n->fbClear, s, RM_TRUE);
    
    if ((n->fbClear && n->fbClear->depthImage != NULL) && (backgroundSceneEnable))
	private_setBackgroundDepthImage(n->fbClear, s, 1, RM_TRUE);

    /* 9/7/05 - the following is not present in the serial rendering code!! */
    if (p->postFBClearBarrierFunc != NULL)
	p->postFBClearBarrierFunc(p);

    /* foil compiler warnings */
    indx = 0;
    rsc = NULL;
}

/*#define GETNODE(a,b) ((RMnode *)(a->objectPool) + b) */
RMnode *
GETNODE(RMcompMgrHdr *a,
	int indx)
{
    RMnode *r;
    int pageNum = rmCompManagerGetPage(indx);
    int offset = rmCompManagerGetOffset(indx);

    r = (RMnode *)(a->objectPool[pageNum]);
    r += offset;
    return r;
}

/* 8/8/04 - temp for debugging */
void
private_rmLightStateConsistencyCheck(char *buf,
				     RMstate *s,
				     RMstateCache *rsc)
{
    int glLightsEnabled = glIsEnabled(GL_LIGHTING);
    int stateLightsEnabled = s->lightingActive == RM_TRUE ? 1 : 0;
    int cacheLightsEnabled = rsc->lightingActive == RM_TRUE ? 1 : 0;
    int agree;

    if (glLightsEnabled)
	agree = stateLightsEnabled && cacheLightsEnabled && glLightsEnabled;
    else 			/* glLights not enabled */
	agree = (!stateLightsEnabled) && (!cacheLightsEnabled) && (!glLightsEnabled);

    fprintf(stderr,"%s ", buf);
    if ( agree )
	fprintf(stderr,"LIGHTs check: agreement. ");
    else
	fprintf(stderr,"LIGHTs check: DISAGREEMENT!! ");
    
    fprintf(stderr," OpenGL = %d, RMstate = %d, RMstateCache = %d \n", glIsEnabled(GL_LIGHTING), (s->lightingActive == RM_TRUE) ? 1 : 0, (rsc->lightingActive == RM_TRUE) ? 1 : 0);
}


void
private_rmPipeDrawDisplayList(RMpipe *p,
			      RMdisplayList *t,
			      RMstate *s,
			      RMstateCache *rsc)
{
    int i;
    extern RMcompMgrHdr *global_RMnodePool;
    RMnode *n=NULL;
    RMstate stateStack[MT_MAX_STATE_STACK_SIZE];
    int stateStackIndx = 0;

    stateStack[stateStackIndx] = *s;
    
    for (i=0;i< t->nOpCodes; i++)
    {
	RMdisplayListOpcode op;
	int indx;
	extern void (*mtRenderFuncs[]) MTWORKPARMS();
	void (*funcPtr) MTWORKPARMS();

	op = (RMdisplayListOpcode)(t->opCodes[i]);
	indx = t->indices[i];

	funcPtr = mtRenderFuncs[(int)op];
	
	switch (op)
	{
	case MT_PRETRAVERSAL_FUNC:
	    n = GETNODE(global_RMnodePool, indx);
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_PRETRAVERSAL_FUNC %s\n", private_rmNodeGetName(n));
#endif
	    break;
	    
	case MT_POSTTRAVERSAL_FUNC:
	    n = GETNODE(global_RMnodePool, indx);
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_POSTTRAVERSAL_FUNC %s\n", private_rmNodeGetName(n));
#endif
	    break;
	    
	case MT_PUSHATTRIB:
	    n = GETNODE(global_RMnodePool, indx);
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_PUSHATTRIB %s\n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_POPATTRIB:
	    /* 8/2002 - no node really needed here */
	    n = NULL; 
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx); 
	    fprintf(stderr,"MT_POPATTRIB %s \n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_DRAW:
	    n = GETNODE(global_RMnodePool, indx);
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_DRAW %s\n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_PUSHLOAD_TEXTURE_MATRIX:
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx);
	    fprintf(stderr,"MT_PUSHLOAD_TEXTURE_MATRIX %s\n", private_rmNodeGetName(n));
#endif
	    rmMatrixCopy((RMmatrix *)&stateStack[stateStackIndx].texture,
			 (RMmatrix *)&(t->matrices[indx].m));
	    glMatrixMode(GL_TEXTURE);
 	    myGlPushMatrix(GL_TEXTURE); 
	    glLoadMatrixf(&(t->matrices[indx].m[0][0]));
	    
	    break;
	    
	case MT_PUSHLOAD_PROJ_MATRIX:
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_PUSHLOAD_PROJ_MATRIX \n" );
#endif
	    rmMatrixCopy((RMmatrix *)&stateStack[stateStackIndx].projection,
			 (RMmatrix *)&(t->matrices[indx].m));
	    glMatrixMode(GL_PROJECTION);
 	    myGlPushMatrix(GL_PROJECTION); 
	    glLoadMatrixf(&(t->matrices[indx].m[0][0]));
	    
	    break;
	    
	case MT_PUSHLOAD_MODELVIEW_MATRIX:
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx);
	    fprintf(stderr,"MT_PUSHLOAD_MODELVIEW_MATRIX \n");
#endif

	    rmMatrixCopy((RMmatrix *)&stateStack[stateStackIndx].modelView,
			 (RMmatrix *)&(t->matrices[indx].m));
	    glMatrixMode(GL_MODELVIEW);
	    myGlPushMatrix(GL_MODELVIEW);  
	    glLoadMatrixf(&(t->matrices[indx].m[0][0]));

	    break;

	case MT_POP_PROJ_MATRIX:
	    n = NULL;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_POP_PROJ_MATRIX \n");
#endif
	    break;

	case MT_POP_MODELVIEW_MATRIX:
	    n = NULL;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_POP_MODELVIEW_MATRIX \n");
#endif
	    break;

	case MT_POP_TEXTURE_MATRIX:
	    n = NULL;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_POP_TEXTURE_MATRIX \n");
#endif
	    break;

	case MT_FBCLEAR:
	    n = GETNODE(global_RMnodePool, indx);
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_FBCLEAR %s\n",private_rmNodeGetName(n));
#endif
	    break;

	case MT_PUSH_TEXTPROPS:
	    n = GETNODE(global_RMnodePool, indx);
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_PUSH_TEXTPROPS %s\n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_POP_TEXTPROPS:
/*	    n = GETNODE(global_RMnodePool, indx); */
	    n = NULL; /* no node needed */
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx);
	    fprintf(stderr,"MT_POP_TEXTPROPS %s\n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_PUSH_STATE:
	    stateStack[stateStackIndx+1] = stateStack[stateStackIndx];
	    stateStackIndx++;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx); 
	    fprintf(stderr,"MT_PUSH_STATE %s\n", private_rmNodeGetName(n));
#endif
	    break;
	    
	case MT_POP_STATE:
	    stateStackIndx--;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx); 
	    fprintf(stderr,"MT_POP_STATE %s\n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_PUSH_STATE_ATTRIB:
	    stateStack[stateStackIndx+1] = stateStack[stateStackIndx];
	    stateStackIndx++;
	    n = GETNODE(global_RMnodePool, indx); 
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"MT_PUSH_STATE_ATTRIB %s\n", private_rmNodeGetName(n));
#endif
	    break;

	case MT_POP_STATE_ATTRIB:
	    stateStackIndx--;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    n = GETNODE(global_RMnodePool, indx); 
	    fprintf(stderr,"MT_POP_STATE_ATTRIB %s\n", private_rmNodeGetName(n));
#endif
	    break;

	default:
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"default \n");
#endif
	    break;
	}

	if (funcPtr != NULL)
	    (*funcPtr)(p, n, stateStack + stateStackIndx, i, rsc);
	    /* 	    (*funcPtr)(p, n, stateStack + stateStackIndx, t, i, rsc); */

#if (DEBUG_LEVEL & DEBUG_GLSTATECHECK)
	private_rmLightStateConsistencyCheck("private_rmPipeDrawDisplayList",stateStack+stateStackIndx, rsc);
#endif

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
	{
	    char buf[64];
	    sprintf(buf, " private_rmDraw OpenGL error ");
	    rmGLGetError(buf);
	}
#endif
	
    }
}

static void
private_setupLeftStereoChannel(RMenum channelFormat)
{
    if (channelFormat == RM_MBUF_STEREO_CHANNEL)
	glDrawBuffer(GL_BACK_LEFT);
    else if (channelFormat == RM_REDBLUE_STEREO_CHANNEL)
	glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
    else		/* assume BLUERED */
	glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
}

static void
private_setupRightStereoChannel(RMenum channelFormat)
{
    if (channelFormat == RM_MBUF_STEREO_CHANNEL)
	glDrawBuffer(GL_BACK_RIGHT);
    else if (channelFormat == RM_REDBLUE_STEREO_CHANNEL)
	glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
    else		/* assume BLUERED */
	glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
}
	    

void
private_rmRender (RMpipe *p,
		  int frameNumber)
{
    RMstateCache *rsc = private_rmStateCacheNew();
    RMstate rState;
    RMenum render3DOpaqueEnable, render3DTransparentEnable, render2DOpaqueEnable;
    RMmultipassDisplayList *mp;
    RMdisplayList *t;
/*     int initMatrixStack = rmPipeGetInitMatrixStackMode(p); not used 6/3/01 wes */
    int dlIndx;
    RMenum channelFormat = rmPipeGetChannelFormat(p);

    /* select even or odd multipass display list as a function of framenum */
    dlIndx = private_rmSelectEvenOddBuffer(frameNumber);

    mp = (RMmultipassDisplayList *)(p->displayLists);

    private_rmStateInit(p, &rState, (RMenum)GL_RENDER, NULL, NULL, NULL, NULL);

    rmPipeGetRenderPassEnable(p, &render3DOpaqueEnable, &render3DTransparentEnable, &render2DOpaqueEnable);

    glDisable(GL_DITHER); /* tmp wes 7/28/02 */

#if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
    rmGLGetError("private_rmRender");
#endif

    /* take care of global stuff */
    {
	glDisable(GL_BLEND);	/* make sure it's turned off */
	glDisable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	glDisable(GL_LIGHTING);
	
	rState.lightingActive = RM_FALSE;
	rsc->lightingActive = RM_FALSE;
	private_rmColorMaterial(&rState, rsc, RM_FALSE);
	
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "private_mtRender: private_mtRender: RMpipe fbclears \n");
#endif
	rState.renderpass = RM_RENDERPASS_ALL;
	rState.renderPassDims = RM_RENDERPASS_ALL;
	
	t = mp->globals[dlIndx];
	rState.which_channel = RM_ALL_CHANNELS;
	private_rmPipeDrawDisplayList(p, t, &rState, rsc);
    }
    
    if (render3DOpaqueEnable == RM_TRUE)
    {
	glDisable(GL_BLEND);	/* make sure it's turned off */
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	
	glDisable(GL_LIGHTING);
	rState.lightingActive = RM_FALSE;
	rsc->lightingActive = RM_FALSE;
	private_rmColorMaterial(&rState, rsc, RM_FALSE);
	
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "private_mtRender: start of 3D opaque pass \n");
#endif
	rState.renderpass = RM_RENDERPASS_OPAQUE;
	rState.renderPassDims = RM_RENDERPASS_3D;
	
	if ((channelFormat == RM_MONO_CHANNEL) ||
	    (channelFormat == RM_OFFSCREEN_MONO_CHANNEL))
	{
	    t = mp->opaque3D[dlIndx];
	    rState.which_channel = RM_ALL_CHANNELS;
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);
	}
	else			/* assume a stereo format */
	{
	    private_setupLeftStereoChannel(channelFormat);

	    rState.which_channel = RM_LEFT_CHANNEL;
	    t = mp->opaque3D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);

#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr, "private_mtRender: start of right-channel, 3D opaque pass \n");
#endif

	    /* re-initialize for drawing the other channel */
	    glDisable(GL_LIGHTING);
	    rState.lightingActive = RM_FALSE;
	    rsc->lightingActive = RM_FALSE;
	    private_rmColorMaterial(&rState, rsc, RM_FALSE);

	    private_setupRightStereoChannel(channelFormat);
	    
	    rState.which_channel = RM_RIGHT_CHANNEL;
	    t = mp->rightOpaque3D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);

	}
    }

    if (render3DTransparentEnable == RM_TRUE)
    {
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_FALSE);

	glDisable(GL_LIGHTING);
	rState.lightingActive = RM_FALSE;
	rsc->lightingActive = RM_FALSE;
	private_rmColorMaterial(&rState, rsc, RM_FALSE);
	
	rState.renderpass = RM_RENDERPASS_TRANSPARENT;
	rState.renderPassDims = RM_RENDERPASS_3D;

#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "private_mtRender: start of 3D transparent pass \n");
#endif
	
	if ((channelFormat == RM_MONO_CHANNEL) ||
	    (channelFormat == RM_OFFSCREEN_MONO_CHANNEL))
	{
	    rState.which_channel = RM_ALL_CHANNELS;
	    t = mp->transparent3D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);
	}
	else
	{
	    private_setupLeftStereoChannel(channelFormat);
	    
	    rState.which_channel = RM_LEFT_CHANNEL;
	    
	    t = mp->transparent3D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);

#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr, "private_mtRender: start of right-channel, 3D transparent pass \n");
#endif
	    /* re-initialize for drawing the other channel */
	    glDisable(GL_LIGHTING);
	    rState.lightingActive = RM_FALSE;
	    rsc->lightingActive = RM_FALSE;
	    private_rmColorMaterial(&rState, rsc, RM_FALSE);
	    
	    private_setupRightStereoChannel(channelFormat);
	    t = mp->rightTransparent3D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);
	}
	glDisable(GL_BLEND);
    }

    if (render2DOpaqueEnable == RM_TRUE)
    {
	glDisable(GL_DEPTH_TEST);
	/* 5/11/02 - make sure actual state is same as in RMstate */
	glDisable(GL_LIGHTING);
	rState.lightingActive = RM_FALSE;
	rsc->lightingActive = RM_FALSE;
	private_rmColorMaterial(&rState, rsc, RM_FALSE);
	
	rState.renderpass = RM_RENDERPASS_OPAQUE;
	rState.renderPassDims = RM_RENDERPASS_2D;
	
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "private_mtRender: start of 2D opaque pass \n");
#endif
	if ((channelFormat == RM_MONO_CHANNEL) ||
	    (channelFormat == RM_OFFSCREEN_MONO_CHANNEL))
	{
	    rState.which_channel = RM_ALL_CHANNELS;
	    t = mp->opaque2D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);
	}
	else
	{
	    private_setupLeftStereoChannel(channelFormat);
	    
	    rState.which_channel = RM_LEFT_CHANNEL;
	    t = mp->opaque2D[dlIndx];
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);

#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr, "private_mtRender: start of right-channel, 2D opaque pass \n");
#endif
	    private_setupRightStereoChannel(channelFormat);
	    
	    rState.which_channel = RM_RIGHT_CHANNEL;
	    t = mp->rightOpaque2D[dlIndx];

	    /* re-initialize for drawing the other channel */
	    glDisable(GL_LIGHTING);
	    rState.lightingActive = RM_FALSE;
	    rsc->lightingActive = RM_FALSE;
	    private_rmColorMaterial(&rState, rsc, RM_FALSE);
	    
	    private_rmPipeDrawDisplayList(p, t, &rState, rsc);
	}
    }
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

    free((void *)rsc);
}

/* EOF */
