/*
 * 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: rmomesh.c,v 1.8 2005/06/26 18:56:33 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.8 $
 * $Log: rmomesh.c,v $
 * Revision 1.8  2005/06/26 18:56:33  wes
 * Added direct manipulation of OpenGL attrib stack inside octmesh draw code.
 * Previous version interacted unnecessarily with RM's state tracking stuff.
 *
 * Revision 1.7  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.6  2005/02/19 16:31:15  wes
 * Distro sync and consolidation.
 * Migrated support for run-time selection of 2D textures as an option
 * if 3D texturing is not supported by the underlying OpenGL.
 *
 * Revision 1.5  2005/01/23 17:05:32  wes
 * Copyright update to 2005.
 *
 * Revision 1.4  2004/01/17 00:59:59  wes
 * Updated copyright line for 2004.
 * Modified rendering code to include a small, texture-size-dependent offset
 * to the texture coords to fix rendering bugs visible when using
 * GL_LINEAR texturing modes on multiple octmeshes located adjacent
 * to one another in space.
 *
 * Revision 1.3  2003/10/03 19:22:05  wes
 * Minor tweakage to automatically generated texture coordinates to avoid
 * rendering artifacts that appear on some platforms in conjunction with
 * using the GL_LINEAR texture environment setting.
 *
 * 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.8  2003/01/24 17:11:20  wes
 * Test - remove OGL state manip from within rmOctmesh() draw routine in order
 * to avoid some bugs induced when used with CR. Long term - the use of state
 * manip needs to be addressed inside this draw routine.
 *
 * Revision 1.7  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.6  2002/06/02 15:16:27  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.5  2002/05/12 15:25:17  wes
 *
 * Changed how number of slices is computed. Previously, the number of
 * slices was computed to be the minimum of grid dimensions in each
 * axis, divided by the model flag. Changed so nslices is now axis-
 * dependent, divided by the model flag. The result is more visually pleasing.
 *
 * Revision 1.4  2002/04/30 19:33:05  wes
 * Updated copyright dates.
 *
 * Revision 1.3  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * 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"

/*
 * this file contains the "draw routine" for the octmesh primitive.
 * for all practical purposes, there are no routines in this file
 * callable from the application level.
 *
 * rmOctmesh() is the entry point for the octmesh draw routine.
 */

#define X_AXIS 0
#define Y_AXIS 1
#define Z_AXIS 2

#define TC_OFFSET 0
/* #define TC_OFFSET 1 */

/* ------- in support of using 2D textures for octmesh rendering ------ */

typedef struct
{
    GLuint *xAxisTextures, *yAxisTextures, *zAxisTextures;
    int nXAxisTextures, nYAxisTextures, nZAxisTextures;
    int tDims[3];
} RMoctmesh2DTextureMgr;

/* -------- end 2d textures stuff --------- */

/* private */
static void
private_freeOctmeshPrimitive2DTextureState(RMoctmesh2DTextureMgr *tm)
{
    if (tm->xAxisTextures != NULL)
    {
	glDeleteTextures(tm->nXAxisTextures, tm->xAxisTextures);
	free((void *)tm->xAxisTextures);
    }
    
    if (tm->yAxisTextures != NULL)
    {
	glDeleteTextures(tm->nYAxisTextures, tm->yAxisTextures);
	free((void *)tm->yAxisTextures);
    }
    
    if (tm->zAxisTextures != NULL)
    {
	glDeleteTextures(tm->nZAxisTextures, tm->zAxisTextures);
	free((void *)tm->zAxisTextures);
    }
    free ((void *)tm);
}

/* private */
void
private_rmOctmeshPrimitiveFree(RMprimitive *p)
{
    if (p->p1 != NULL)
	private_freeOctmeshPrimitive2DTextureState((RMoctmesh2DTextureMgr *)(p->p1));
    p->p1 = NULL;
}

/* private */
static int
private_OctmeshTextureDimsIdentical(RMoctmesh2DTextureMgr *tmgr,
				  int newDims[3])
{
    if ((tmgr->tDims[0] == newDims[0]) && (tmgr->tDims[1] == newDims[1]) && (tmgr->tDims[2] == newDims[2]))
	return 1;
    else
	return 0;
}

/* PRIVATE */
static void
scan_xaxis (float dot,
	    int nslices,
	    float *v,
	    int *dims,
	    int *tdims)
{
    int    i;
    int    stepsize = 2;
    int    ix, ixincr;
    int    kmin, kmax, jmin, jmax;
    float *x, *y, *z;
    float  txstart, txincr;

    /* tc's are in (u,w) order (1,0), (0,0), (0,1), (1,1) */
    RMvertex3D tc[4] = {{0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 1.0}}; 
    RMvertex3D lv[4];
    RMvertex3D n;

#if TC_OFFSET
    float yEps, zEps;
    
    /*
     * Jan 2004 - compute a slight offset added to texture coordinates
     * in the plane facing the viewer. This is needed to fix some "cracks"
     * that are visible when rendering multiple octmeshes that are
     * located next to one another and when using the GL_LINEAR texture
     * filter mode.
     */

    yEps = -0.5/(float)(tdims[1]);
    zEps = -0.5/(float)(tdims[2]);
    
    tc[0].y -= yEps;
    tc[0].z += zEps;
    tc[1].y -= yEps;
    tc[1].z -= zEps;
    tc[2].y += yEps;
    tc[2].z -= zEps;
    tc[3].y += yEps;
    tc[3].z += zEps;
#else
    tdims = NULL; 		/* foil compiler warning */
#endif
    
    kmin = jmin = 0;
    kmax = dims[2] - 1;
    jmax = dims[1] - 1;

    stepsize = dims[0] / nslices;

    if (dot < 0)
    {
	ix = dims[0] - 1;
	ixincr = -stepsize;
	txstart = 1.0;
	txincr = -(float)(stepsize) / (float)(dims[0] - 1);
    }
    else
    {
	ix = 0;
	ixincr = stepsize;
	txstart = 0.0;
	txincr = (float)(stepsize) / (float)(dims[0] - 1);
    }
	
    x = v;
    y = v + dims[0];
    z = y + dims[1];

    lv[0].y = y[jmin];
    lv[0].z = z[kmax];
    
    lv[1].y = y[jmin];
    lv[1].z = z[kmin];
    
    lv[2].y = y[jmax];
    lv[2].z = z[kmin];
    
    lv[3].y = y[jmax];
    lv[3].z = z[kmax];

    n.x = 1.0;
    n.y = n.z = 0.0;

    for (i = 0; i < dims[0]; i+= stepsize, ix+= ixincr, txstart += txincr) 
    {
	lv[0].x = x[ix];
	lv[1].x = x[ix];
	lv[2].x = x[ix];
	lv[3].x = x[ix];

	tc[0].x = txstart;
	tc[1].x = txstart;
	tc[2].x = txstart;
	tc[3].x = txstart;
    
	glBegin(GL_QUADS);

	glNormal3fv((float *)&n);
	glTexCoord3fv((float *)tc);
	glVertex3fv((float *)lv);
    
	glTexCoord3fv((float *)(tc + 1));
	glVertex3fv((float *)(lv + 1));
    
	glTexCoord3fv((float *)(tc + 2));
	glVertex3fv((float *)(lv + 2));
    
	glTexCoord3fv((float *)(tc + 3));
	glVertex3fv((float *)(lv + 3));

	glEnd();
    }
}


/* PRIVATE */
static void
scan_yaxis (float dot,
	    int nslices,
	    float *v,
	    int *dims,
	    int *tdims)
{
    int    i;
    int    stepsize;
    int    iy, iyincr;
    int    kmin, kmax, imin, imax;
    float *x, *y, *z;
    float  tystart, tyincr;
    
    /* tc's are in (u,w) order (0,0), (1,0), (1,1), (0,1) */
    RMvertex3D tc[4] = {{0.0, 0.0, 1.0}, {1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
    RMvertex3D lv[4];
    RMvertex3D n;

#if TC_OFFSET
    float xEps, zEps;
    
    /*
     * Jan 2004 - compute a slight offset added to texture coordinates
     * in the plane facing the viewer. This is needed to fix some "cracks"
     * that are visible when rendering multiple octmeshes that are
     * located next to one another and when using the GL_LINEAR texture
     * filter mode.
     */
    xEps = -0.5/(float)(tdims[0]);
    zEps = -0.5/(float)(tdims[2]);
    
    tc[0].x -= xEps;
    tc[0].z += zEps; 

    tc[1].x += xEps;
    tc[1].z += zEps; 
    
    tc[2].x += xEps;
    tc[2].z -= zEps; 
    
    tc[3].x -= xEps;
    tc[3].z -= zEps;
#else
    tdims = NULL; 		/* foil compiler warning */
#endif

    imin = kmin = 0;
    kmax = dims[2] - 1;
    imax = dims[0] - 1;

    stepsize = dims[1] / nslices;
    if (stepsize == 0)
        stepsize = 1;

    if (dot > 0)
    {
	iy = dims[1] - 1;
	iyincr = -stepsize;
	tystart = 1.0;
	tyincr = -(float)(stepsize) / (float)(dims[1] - 1);
    }
    else
    {
	iy = 0;
	iyincr = stepsize;
	tystart = 0.0;
	tyincr = (float)(stepsize) / (float)(dims[1] - 1);
    }
	
    x = v;
    y = v + dims[0];
    z = y + dims[1];

    lv[0].x = x[imin];
    lv[0].z = z[kmax];
    
    lv[1].x = x[imax];
    lv[1].z = z[kmax];
    
    lv[2].x = x[imax];
    lv[2].z = z[kmin];
    
    lv[3].x = x[imin];
    lv[3].z = z[kmin];

    n.y = 1.0;
    n.x = n.z = 0.0;

    for (i = 0; i < dims[1]; i+= stepsize, iy += iyincr, tystart += tyincr)
    {
	lv[0].y = y[iy];
	lv[1].y = y[iy];
	lv[2].y = y[iy];
	lv[3].y = y[iy];

	tc[0].y = tystart;
	tc[1].y = tystart;
	tc[2].y = tystart;
	tc[3].y = tystart;
#if 0	
	tc[0].y = (float)i / (float)(p->vsize - 1);
	tc[1].y = (float)i / (float) (p->vsize - 1);
	tc[2].y = (float)i / (float)(p->vsize - 1);
	tc[3].y = (float)i / (float)(p->vsize - 1);
#endif    
	glBegin(GL_QUADS);

	glNormal3fv((float *)&n);
	glTexCoord3fv((float *)tc);
	glVertex3fv((float *)lv);
    
	glTexCoord3fv((float *)(tc + 1));
	glVertex3fv((float *)(lv + 1));
    
	glTexCoord3fv((float *)(tc + 2));
	glVertex3fv((float *)(lv + 2));
    
	glTexCoord3fv((float *)(tc + 3));
	glVertex3fv((float *)(lv + 3));

	glEnd();
    }
}


/* PRIVATE */
static void
scan_zaxis (float dot,
	    int nslices,
	    float *v,
	    int *dims,
	    int *tdims)
{
    int    i;
    int    stepsize;
    int    iz, izincr;
    int    imin, imax, jmin, jmax;
    float *x, *y, *z;
    float  tzstart, tzincr;

    /* tc's are in (u,w) order (0,0), (1,0), (1,1), (0,1) */
    RMvertex3D tc[4] = {{0.0, 0.0, 0.5}, {1.0, 0.0, 0.5}, {1.0, 1.0, 0.5}, {0.0, 1.0, 0.5}}; 
    RMvertex3D lv[4];
    RMvertex3D n;

#if TC_OFFSET
    float xEps, yEps;
    
    /*
     * Jan 2004 - compute a slight offset added to texture coordinates
     * in the plane facing the viewer. This is needed to fix some "cracks"
     * that are visible when rendering multiple octmeshes that are
     * located next to one another and when using the GL_LINEAR texture
     * filter mode.
     */
    xEps = -0.5/(float)(tdims[0]);
    yEps = -0.5/(float)(tdims[1]);
    tc[0].x -= xEps;
    tc[0].y -= yEps;
    tc[1].x += xEps;
    tc[1].y -= yEps;
    tc[2].x += xEps;
    tc[2].y += yEps;
    tc[3].x -= xEps;
    tc[3].y += yEps;
#else
    tdims = NULL; 		/* foil compiler warning */
#endif

    imin = jmin = 0;
    imax = dims[0];
    jmax = dims[1];
    stepsize = dims[2] / nslices;

    if (dot < 0)
    {
	iz = dims[2] - 1;
	izincr = -stepsize;
	tzstart = 1.0;
	tzincr = -(float)stepsize / (float)(dims[2] - 1); 
	
    }
    else
    {
	iz = 0;
	izincr = stepsize;
	tzstart = 0.;
	tzincr = (float)stepsize / (float)(dims[2] - 1); 
	
    }
	
    x = v;
    y = v + dims[0];
    z = y + dims[1];

    lv[0].x = x[imin];
    lv[0].y = y[jmin];
    
    lv[1].x = x[imax - 1];
    lv[1].y = y[jmin];
    
    lv[2].x = x[imax - 1];
    lv[2].y = y[jmax - 1];
    
    lv[3].x = x[imin];
    lv[3].y = y[jmax - 1];

    n.x = n.y = 0.0;
    n.z = 1.0;

    for (i = 0; i < dims[2]; i+= stepsize, iz += izincr, tzstart += tzincr)
    {
	lv[0].z = z[iz];
	lv[1].z = z[iz];
	lv[2].z = z[iz];
	lv[3].z = z[iz];
	
	tc[0].z = tzstart;
	tc[1].z = tzstart;
	tc[2].z = tzstart;
	tc[3].z = tzstart;
    
	glBegin(GL_QUADS);

	glNormal3fv((float *)&n);
	glTexCoord3fv((float *)tc);
	glVertex3fv((float *)lv);
    
	glTexCoord3fv((float *)(tc + 1));
	glVertex3fv((float *)(lv + 1));
    
	glTexCoord3fv((float *)(tc + 2));
	glVertex3fv((float *)(lv + 2));
    
	glTexCoord3fv((float *)(tc + 3));
	glVertex3fv((float *)(lv + 3));

	glEnd();
    }
}


static void
private_loadOmesh2DTexture(RMtexture *t,
			   int width,
			   int height,
			   const GLvoid *texelData)
{
    GLenum dstGLTexelFormat; 
    rmTextureGetGLTexelFormat(t, &dstGLTexelFormat);
	    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, t->wrap_mode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, t->wrap_mode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, t->mag_filter_mode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, t->min_filter_mode);
	    
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, t->envMode);
	     
    glTexImage2D(GL_TEXTURE_2D, 0,
		 dstGLTexelFormat,
		 width, height,
		 t->borderWidth,
		 private_rmImageGetOGLFormat(t->images[0]),
		 private_rmImageGetOGLType(t->images[0]),
		 (const GLvoid *)(texelData));
}

static void
scanXAxis2DTextures (RMprimitive *p,
		     float dot,
		     int nslices,
		     float *v,
		     int *gdims, /* grid dims */
		     RMtexture *t,
		     RMenum forceRebuildTextures,
		     int renderingMode)
{
    int    i;
    int    stepsize = 2;
    int    ix, ixincr;
    int    kmin, kmax, jmin, jmax;
    float *x, *y, *z;
    float  txstart, txincr;
    unsigned char *wbuf=NULL;
    int    buildTextures=0;
    int    istart, iend, iincr;
    RMoctmesh2DTextureMgr *tm = (RMoctmesh2DTextureMgr *)(p->p1);
    int    tdims[3];

    /* tc's are in (u,w) order (1,0), (0,0), (0,1), (1,1) */
    RMvertex2D tc[4] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}};
    RMvertex3D lv[4];
    RMvertex3D n;

    memcpy((void *)tdims, (void *)tm->tDims, sizeof(int)*3);
    
#if TC_OFFSET
    /*
     * Jan 2004 - compute a slight offset added to texture coordinates
     * in the plane facing the viewer. This is needed to fix some "cracks"
     * that are visible when rendering multiple octmeshes that are
     * located next to one another and when using the GL_LINEAR texture
     * filter mode.
     */
    float yEps, zEps;
    
    yEps = -0.5/(float)(tdims[1]);
    zEps = -0.5/(float)(tdims[2]);
    
    tc[0].x += zEps;
    tc[0].y -= yEps;
    tc[1].x -= zEps;
    tc[1].y -= yEps;
    tc[2].x -= zEps;
    tc[2].y += yEps;
    tc[3].x += zEps;
    tc[3].y += yEps;
#endif

    if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
    {
	/* use the one existing texture object assigned to this axis */
	glBindTexture(GL_TEXTURE_2D, tm->xAxisTextures[0]);
	glEnable(GL_TEXTURE_2D);
	buildTextures = 1;
    }

    kmin = jmin = 0;
    kmax = gdims[2] - 1;
    jmax = gdims[1] - 1;

    stepsize = gdims[0] / nslices;

    if (dot < 0)
    {
	ix = gdims[0] - 1;
	ixincr = -stepsize;
	txstart = 1.0;
	txincr = -(float)(stepsize) / (float)(gdims[0] - 1);
	
	istart = nslices-1;
	iend = -1;
	iincr = -1;
    }
    else
    {
	ix = 0;
	ixincr = stepsize;
	txstart = 0.0;
	txincr = (float)(stepsize) / (float)(gdims[0] - 1);
	
	istart = 0;
	iend = nslices;
	iincr = 1;
    }
	
    x = v;
    y = v + gdims[0];
    z = y + gdims[1];

    lv[0].y = y[jmin];
    lv[0].z = z[kmin];
    
    lv[1].y = y[jmin];
    lv[1].z = z[kmax];
    
    lv[2].y = y[jmax];
    lv[2].z = z[kmax];
    
    lv[3].y = y[jmax];
    lv[3].z = z[kmin];

    n.x = 1.0;
    n.y = n.z = 0.0;

    
    if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
    {
	if (tm->xAxisTextures == NULL)
	{
	    tm->xAxisTextures = (GLuint *)calloc(tdims[0], sizeof(GLuint));
	    glGenTextures(tdims[0], tm->xAxisTextures);
	    buildTextures = 1;
	}
	else if (forceRebuildTextures == RM_TRUE)
	    buildTextures = 1;
    }

    wbuf = (unsigned char *)malloc(sizeof(unsigned char)*tdims[2]*tdims[1]*(t->images[0]->bytes_per_component)*(t->images[0]->elements));

    for (i = istart; i != iend; i+=iincr, ix+= ixincr, txstart += txincr) /* for each plane */
    {
	
	if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
	    glBindTexture(GL_TEXTURE_2D, tm->xAxisTextures[i]);
	
	if (buildTextures == 1)
	{
	    int incr = t->images[0]->bytes_per_component*t->images[0]->elements;
	    int y, z;
	    int bytesPerScanline = rmImageGetBytesPerScanline(t->images[0]);
	    int xOffset = ix*incr; 
	    unsigned char *texelData = (unsigned char *)private_rmImageGetPixelData(t->images[0]);
	    int zOffset=0;
	    int yOffset=0;
	    int dstIndx = 0;
	    
	    texelData += xOffset;

	    for (y=0; y<tdims[1]; y++, yOffset += bytesPerScanline)
	    {
		zOffset=0;
		/* the following doesn't take into account scanline padding */
		dstIndx = y * tdims[2] * t->images[0]->bytes_per_component * t->images[0]->elements;
	    
		for (z=0;z<tdims[2];z++, zOffset += (tdims[1]*bytesPerScanline), dstIndx += incr)
		{
		    int totalOffset;
		    totalOffset = yOffset + zOffset;
		    memcpy(wbuf+dstIndx, texelData+totalOffset, incr);
		}
	    }

	    if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
		private_loadOmesh2DTexture(t, tdims[2], tdims[1], wbuf);
	    else if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
		glTexSubImage2D(GL_TEXTURE_2D, 0,
				0, 0,
				tdims[2], tdims[1],
				private_rmImageGetOGLFormat(t->images[0]),
				private_rmImageGetOGLType(t->images[0]),
				(const GLvoid *)(wbuf));
	}


	if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
	    glEnable(GL_TEXTURE_2D);
	
	lv[0].x = x[ix];
	lv[1].x = x[ix];
	lv[2].x = x[ix];
	lv[3].x = x[ix];

	glBegin(GL_QUADS);

	glNormal3fv((float *)&n);
	glTexCoord2fv((float *)tc);
	glVertex3fv((float *)lv);
    
	glTexCoord2fv((float *)(tc + 1));
	glVertex3fv((float *)(lv + 1));
    
	glTexCoord2fv((float *)(tc + 2));
	glVertex3fv((float *)(lv + 2));
    
	glTexCoord2fv((float *)(tc + 3));
	glVertex3fv((float *)(lv + 3));

	glEnd();
    }

    if (wbuf != NULL)
	free((void *)wbuf);
}

/* PRIVATE */
static void
scanYAxis2DTextures (RMprimitive *p,
		     float dot,
		     int nslices,
		     float *v,
		     int *gdims,
		     RMtexture *t,
		     RMenum forceRebuildTextures,
		     int renderingMode)
{
    int    i;
    int    stepsize;
    int    iy, iyincr;
    int    kmin, kmax, imin, imax;
    float *x, *y, *z;
    float  tystart, tyincr;
    unsigned char *wbuf = NULL;
    int    buildTextures=0;
    int    istart, iend, iincr;
    RMoctmesh2DTextureMgr *tm = (RMoctmesh2DTextureMgr *)(p->p1);
    int    tdims[3];
    
    /* tc's are in (u,w) order (0,0), (1,0), (1,1), (0,1) */
    RMvertex2D tc[4] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}};
    RMvertex3D lv[4];
    RMvertex3D n;

    memcpy((void *)tdims, (void *)(tm->tDims), sizeof(int)*3);
    
#if TC_OFFSET
    float xEps, zEps;
    /*
     * Jan 2004 - compute a slight offset added to texture coordinates
     * in the plane facing the viewer. This is needed to fix some "cracks"
     * that are visible when rendering multiple octmeshes that are
     * located next to one another and when using the GL_LINEAR texture
     * filter mode.
     */
    xEps = -0.5/(float)(tdims[0]);
    zEps = -0.5/(float)(tdims[2]);

    tc[0].x -= xEps;
    tc[0].y += zEps; 

    tc[1].x += xEps;
    tc[1].y += zEps; 
    
    tc[2].x += xEps;
    tc[2].y -= zEps; 
    
    tc[3].x -= xEps;
    tc[3].y -= zEps;
#endif

    if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
    {
	/* use the one existing texture object assigned to this axis */
	glBindTexture(GL_TEXTURE_2D, tm->yAxisTextures[0]);
	glEnable(GL_TEXTURE_2D);
	buildTextures = 1;
    }

    imin = kmin = 0;
    kmax = gdims[2] - 1;
    imax = gdims[0] - 1;

    stepsize = gdims[1] / nslices;
    if (stepsize == 0)
        stepsize = 1;

    if (dot > 0)
    {
	iy = gdims[1] - 1;
	iyincr = -stepsize;
	tystart = 1.0;
	tyincr = -(float)(stepsize) / (float)(gdims[1] - 1);
	
	istart = nslices-1;
	iend = -1;
	iincr = -1;
    }
    else
    {
	iy = 0;
	iyincr = stepsize;
	tystart = 0.0;
	tyincr = (float)(stepsize) / (float)(gdims[1] - 1);
	
	istart = 0;
	iend = nslices;
	iincr = 1;
    }
	
    x = v;
    y = v + gdims[0];
    z = y + gdims[1];

    lv[0].x = x[imin];
    lv[0].z = z[kmin];
    
    lv[1].x = x[imax];
    lv[1].z = z[kmin];
    
    lv[2].x = x[imax];
    lv[2].z = z[kmax];
    
    lv[3].x = x[imin];
    lv[3].z = z[kmax];

    n.y = 1.0;
    n.x = n.z = 0.0;

    if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
    {
	if (tm->yAxisTextures == NULL)
	{
	    tm->yAxisTextures = (GLuint *)calloc(tdims[1], sizeof(GLuint));
	    glGenTextures(tdims[1], tm->yAxisTextures);
	    buildTextures = 1;
	}
	else if (forceRebuildTextures == RM_TRUE)
	    buildTextures = 1;
    }
    
    wbuf = (unsigned char *)malloc(sizeof(unsigned char *)*tdims[0]*tdims[2]*rmImageGetBytesPerScanline(t->images[0]));
				   
    for (i = istart; i != iend; i+= iincr, iy += iyincr, tystart += tyincr)
    {
	int z;
	int bytesPerScanline = rmImageGetBytesPerScanline(t->images[0]);
	int offset = iy * bytesPerScanline;
	int depthOffset=0;
	unsigned char *texelData = (unsigned char *)private_rmImageGetPixelData(t->images[0]);
	    
	if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
	    glBindTexture(GL_TEXTURE_2D, tm->yAxisTextures[i]);
	
	if (buildTextures == 1)
	{
	    texelData += offset;

	    for (z=0;z<tdims[2];z++)
	    {
		memcpy(wbuf+z*bytesPerScanline, texelData+depthOffset, bytesPerScanline);
		depthOffset +=  tdims[1] * bytesPerScanline;
	    }

	    if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
		private_loadOmesh2DTexture(t, tdims[0], tdims[2], wbuf);
	    else if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
		glTexSubImage2D(GL_TEXTURE_2D, 0,
				0, 0,
				tdims[0], tdims[2],
				private_rmImageGetOGLFormat(t->images[0]),
				private_rmImageGetOGLType(t->images[0]),
				(const GLvoid *)(wbuf));
	}

	if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
	    glEnable(GL_TEXTURE_2D);
	
	lv[0].y = y[iy];
	lv[1].y = y[iy];
	lv[2].y = y[iy];
	lv[3].y = y[iy];

	glBegin(GL_QUADS);

	glNormal3fv((float *)&n);
	glTexCoord2fv((float *)tc);
	glVertex3fv((float *)lv);
    
	glTexCoord2fv((float *)(tc + 1));
	glVertex3fv((float *)(lv + 1));
    
	glTexCoord2fv((float *)(tc + 2));
	glVertex3fv((float *)(lv + 2));
    
	glTexCoord2fv((float *)(tc + 3));
	glVertex3fv((float *)(lv + 3));

	glEnd();
    }

    if (wbuf != NULL)
	free((void *)wbuf);
}


/* PRIVATE */
static void
scanZAxis2DTextures (RMprimitive *p,
		     float dot,
		     int nslices,
		     float *v,
		     int *gdims,
		     RMtexture *t,
		     RMenum forceRebuildTextures,
		     int renderingMode)
{
    int    i;
    int    stepsize;
    int    iz, izincr;
    int    imin, imax, jmin, jmax;
    float *x, *y, *z;
    float  tzstart, tzincr;
    int    buildTextures=0;
    int    istart, iend, iincr;
    RMoctmesh2DTextureMgr *tm = (RMoctmesh2DTextureMgr *)(p->p1);
    int    tdims[3];

    /* tc's are in (u,w) order (0,0), (1,0), (1,1), (0,1) */
    RMvertex2D tc[4] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}}; 
    RMvertex3D lv[4];
    RMvertex3D n;

    memcpy((void *)tdims, (void *)(tm->tDims), sizeof(int)*3);
    
#if TC_OFFSET
    float xEps, yEps;
    /*
     * Jan 2004 - compute a slight offset added to texture coordinates
     * in the plane facing the viewer. This is needed to fix some "cracks"
     * that are visible when rendering multiple octmeshes that are
     * located next to one another and when using the GL_LINEAR texture
     * filter mode.
     */
    xEps = -0.5/(float)(tdims[0]);
    yEps = -0.5/(float)(tdims[1]);
    tc[0].x -= xEps;
    tc[0].y -= yEps;
    tc[1].x += xEps;
    tc[1].y -= yEps;
    tc[2].x += xEps;
    tc[2].y += yEps;
    tc[3].x -= xEps;
    tc[3].y += yEps;
#endif

    if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
    {
	/* use the one existing texture object assigned to this axis */
	glBindTexture(GL_TEXTURE_2D, tm->zAxisTextures[0]);
	glEnable(GL_TEXTURE_2D);
	buildTextures = 1;
    }

    imin = jmin = 0;
    imax = gdims[0];
    jmax = gdims[1];
    stepsize = gdims[2] / nslices;

    if (dot < 0)
    {
	iz = gdims[2] - 1;
	izincr = -stepsize;
	tzstart = 1.0;
	tzincr = -(float)stepsize / (float)(gdims[2] - 1);
	
	istart = nslices-1;
	iend = -1;
	iincr = -1;
    }
    else
    {
	iz = 0;
	izincr = stepsize;
	tzstart = 0.;
	tzincr = (float)stepsize / (float)(gdims[2] - 1);
	
	istart = 0;
	iend = nslices;
	iincr = 1;
    }
	
    x = v;
    y = v + gdims[0];
    z = y + gdims[1];

    lv[0].x = x[imin];
    lv[0].y = y[jmin];
    
    lv[1].x = x[imax - 1];
    lv[1].y = y[jmin];
    
    lv[2].x = x[imax - 1];
    lv[2].y = y[jmax - 1];
    
    lv[3].x = x[imin];
    lv[3].y = y[jmax - 1];

    n.x = n.y = 0.0;
    n.z = 1.0;

    if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
    {
	if (tm->zAxisTextures == NULL)
	{
	    tm->zAxisTextures = (GLuint *)calloc(tdims[2], sizeof(GLuint));
	    glGenTextures(tdims[2], tm->zAxisTextures);
	    buildTextures = 1;
	}
	else if (forceRebuildTextures == RM_TRUE)
	    buildTextures = 1;
    }
    
    for (i = istart; i != iend; i+= iincr, iz += izincr, tzstart += tzincr)
    {
	int offset = iz * tdims[1] * rmImageGetBytesPerScanline(t->images[0]);
	unsigned char *texelData = (unsigned char *)private_rmImageGetPixelData(t->images[0]);
	    
	texelData += offset;
	
	if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
	{
	    glBindTexture(GL_TEXTURE_2D, tm->zAxisTextures[i]);
	    if (buildTextures == 1)
		private_loadOmesh2DTexture(t, tdims[0], tdims[1], texelData);
	    glEnable(GL_TEXTURE_2D);
	}
	else if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
	    glTexSubImage2D(GL_TEXTURE_2D, 0,
			    0, 0,
			    tdims[0], tdims[1],
			    private_rmImageGetOGLFormat(t->images[0]),
			    private_rmImageGetOGLType(t->images[0]),
			    (const GLvoid *)texelData);
	
	lv[0].z = z[iz];
	lv[1].z = z[iz];
	lv[2].z = z[iz];
	lv[3].z = z[iz];

	glBegin(GL_QUADS);

	glNormal3fv((float *)&n); 
	glTexCoord2fv((float *)tc);
	glVertex3fv((float *)lv);
    
	glTexCoord2fv((float *)(tc + 1));
	glVertex3fv((float *)(lv + 1));
    
	glTexCoord2fv((float *)(tc + 2));
	glVertex3fv((float *)(lv + 2));
    
	glTexCoord2fv((float *)(tc + 3));
	glVertex3fv((float *)(lv + 3));

	glEnd();
    }
}
/* private */
void
private_renderOctmeshUsing3DTexture(int bestAxis, 
				    float dot, 
				    int divisor,
				    float *v,
				    int *dims,
				    int textureDims[3])
{
    int nslices;
    switch (bestAxis)
    {
    case X_AXIS:
	nslices = dims[0]/divisor;
	if (nslices == 0)
	    nslices = 1;
	scan_xaxis(dot, nslices, v, dims, textureDims);
	break;
	  
    case Y_AXIS:
	nslices = dims[1]/divisor;
	if (nslices == 0)
	    nslices = 1;
	scan_yaxis(dot, nslices, v, dims, textureDims);
	break;
	  
    case Z_AXIS:
	nslices = dims[2]/divisor;
	if (nslices == 0)
	    nslices = 1;
	scan_zaxis(dot, nslices, v, dims, textureDims);
	break;
	
    default: /* bogus axis enum */
	break;
    }
}

void
private_build2DTextureObjects(RMprimitive *p,
			      RMtexture *t,
			      int tDims[3],
			      int renderingMode)
{
    RMoctmesh2DTextureMgr *tmgr;

    /*
     * This routine is being called because the application loaded new
     * texel data. We can avoid rebuilding the OpenGL texture objects
     * (and later just update the ones we have) so long as
     * the texture dimensions have not changed.
     *
     * If the texture dimensions have changed, we will just rebuild all
     * the textures from scratch.
     */
    
    if ((p->p1 == NULL) ||
	(!private_OctmeshTextureDimsIdentical((RMoctmesh2DTextureMgr *)(p->p1), tDims)))
    {
	if (p->p1 != NULL)
	{
	    /* must be that texture dims are different. need to rebuild from scratch */
	    private_freeOctmeshPrimitive2DTextureState((RMoctmesh2DTextureMgr *)(p->p1));
	    p->p1 = NULL;
	}

	/* build new one */
	tmgr = (RMoctmesh2DTextureMgr *)calloc(1, sizeof(RMoctmesh2DTextureMgr));
	if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
	{
	    tmgr->nXAxisTextures = tmgr->nYAxisTextures = tmgr->nZAxisTextures = 1;
	    tmgr->xAxisTextures = (GLuint *)calloc(tmgr->nXAxisTextures, sizeof(GLuint));
	    tmgr->yAxisTextures = (GLuint *)calloc(tmgr->nYAxisTextures, sizeof(GLuint));
	    tmgr->zAxisTextures = (GLuint *)calloc(tmgr->nYAxisTextures, sizeof(GLuint));
	    glGenTextures(tmgr->nXAxisTextures, tmgr->xAxisTextures);
	    glGenTextures(tmgr->nYAxisTextures, tmgr->yAxisTextures);
	    glGenTextures(tmgr->nZAxisTextures, tmgr->zAxisTextures);
	}
	else if (renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE)
	{
	    tmgr->nXAxisTextures = tDims[0];
	    tmgr->nYAxisTextures = tDims[1];
	    tmgr->nZAxisTextures = tDims[2];

	    /* NULL out the GLuint's. The NULL is detected during rendering
	       and is used to build the textures the first time they are
	       needed,  but then simply invoked thereafter. */
	    tmgr->xAxisTextures = tmgr->yAxisTextures = tmgr->zAxisTextures = NULL;
	}

	memcpy(tmgr->tDims, tDims, sizeof(int)*3);
	
	p->p1 = (void *)tmgr;
    }

    if (renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY)
    {
	/*
	 * In the minimum memory case, we need to go ahead an build the
	 * texture object so that we can use glTexSubImage later on to update
	 * it during rendering.
	 */
	GLenum dstGLTexelFormat, srcGLTexelFormat, srcGLTexelType;
	GLint border = t->borderWidth;

	tmgr = (RMoctmesh2DTextureMgr *)(p->p1);

	rmTextureGetGLTexelFormat(t, &dstGLTexelFormat);
	srcGLTexelFormat = private_rmImageGetOGLFormat(t->images[0]);
	srcGLTexelType = private_rmImageGetOGLType(t->images[0]);

	/* build the x-axis texture -- slices of y/z */
	glBindTexture(GL_TEXTURE_2D, tmgr->xAxisTextures[0]);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, t->wrap_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, t->wrap_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, t->mag_filter_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, t->min_filter_mode);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, t->envMode);
	     
	if (t->blendColor != NULL)
	    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat *)(t->blendColor));

	/* send down a blob of texel data to have OpenGL build a texture object */
	glTexImage2D(GL_TEXTURE_2D, 0,
		     dstGLTexelFormat,
		     tDims[2], tDims[1],
		     border,
		     srcGLTexelFormat,
		     srcGLTexelType,
		     (const GLvoid *)(rmImageGetPixelData(t->images[0])));
    
	/* build the y-axis texture -- slices of x/z */
	glBindTexture(GL_TEXTURE_2D, tmgr->yAxisTextures[0]);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, t->wrap_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, t->wrap_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, t->mag_filter_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, t->min_filter_mode);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, t->envMode);
	     
	if (t->blendColor != NULL)
	    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat *)(t->blendColor));

	/* send down a blob of texel data to have OpenGL build a texture object */
	glTexImage2D(GL_TEXTURE_2D, 0,
		     dstGLTexelFormat,
		     tDims[0], tDims[2],
		     border,
		     srcGLTexelFormat,
		     srcGLTexelType,
		     (const GLvoid *)(rmImageGetPixelData(t->images[0])));
    
	/* build the z-axis texture -- slices of x/y*/
	glBindTexture(GL_TEXTURE_2D, tmgr->zAxisTextures[0]);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, t->wrap_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, t->wrap_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, t->mag_filter_mode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, t->min_filter_mode);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, t->envMode);
	     
	if (t->blendColor != NULL)
	    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat *)(t->blendColor));

	/* send down a blob of texel data to have OpenGL build a texture object */
	glTexImage2D(GL_TEXTURE_2D, 0,
		     dstGLTexelFormat,
		     tDims[0], tDims[1],
		     border,
		     srcGLTexelFormat,
		     srcGLTexelType,
		     (const GLvoid *)(rmImageGetPixelData(t->images[0])));
    }
    
}

/* private */
void
private_renderOctmeshUsing2DTextures(RMprimitive *p, 
				     int bestAxis, 
				     float dot, 
				     int divisor,
				     float *v,
				     int *dims,
				     int textureDims[3],
				     RMtexture *t,
				     RMenum rebuildTextures,
				     int renderingMode)
{
    int nslices;
    RMvisMap *vmap= NULL;

    /* enable texturing */
    glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT);  

    /* enable pixel transfer if needed */
    vmap = private_rmImageGetVismap(t->images[0]);
    if (vmap != NULL)
	private_rmSetPixelTransferMode(vmap);
    else
	private_rmUnsetPixelTransferMode();
	
#if HAVE_3D_TEXTURES
    /* turn off 3d texturing if supported */
    glDisable(GL_TEXTURE_3D);
#endif

    if (rebuildTextures == RM_TRUE)
	private_build2DTextureObjects(p, t, textureDims, renderingMode);
    
    /* step through slice planes */
    switch (bestAxis)
    {
    case X_AXIS:

	nslices = dims[0]/divisor;
	if (nslices == 0)
	    nslices = 1;

	scanXAxis2DTextures(p, dot, nslices, v, dims, t, rebuildTextures, renderingMode);

	break;
	  
    case Y_AXIS:

	nslices = dims[1]/divisor;
	if (nslices == 0)
	    nslices = 1;
	
	scanYAxis2DTextures(p, dot, nslices, v, dims, t, rebuildTextures, renderingMode);
	break;
	  
    case Z_AXIS:
	nslices = dims[2]/divisor;
	if (nslices == 0)
	    nslices = 1;
	scanZAxis2DTextures(p, dot, nslices, v, dims, t, rebuildTextures, renderingMode);
	break;
	
    default: /* bogus axis enum */
	break;
    }

    /* delete the texture */
    glFinish();

    /* restore OpenGL state */
    glPopAttrib(); 
}

/* PRIVATE */
void
rmOctmesh  OGLPRIMPARMLIST() 
{
    int                  i, best_axis;
    int                  divisor;
    int                  nverts, vstride; 	/* for verts */
    int                  sstride;		/* for config */
    int                 *dims;
    float                dot;
    float               *v;
    RMprimitiveDataBlob *vblob;			/* for verts */
    RMprimitiveDataBlob *sblob; 		/* for config */
    int                  textureDims[3];
    int                  renderingMode;

    /* foil compiler warnings */
    r = NULL;
    renderPipe = NULL;
    rsc = NULL;

    /* figure out which axis is most perpindicular to the line-of-sight */
    {
	RMmatrix tmp;
	float    tv[3][4] = {{1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0},{0.0, 0.0, 1.0, 0.0}};
	double   x, y, z;
		       
	rmMatrixMultiply(&(s->modelView), &(s->projection), &tmp);
	
	for (i = 0; i < 3; i++)
	{
	    rmPoint4MatrixTransform(tv[i], &tmp, tv[i]);
	    rmVertex3DNormalize((RMvertex3D *)tv[i]);
	}

	x = fabs(tv[0][2]);
	y = fabs(tv[1][2]);
	z = fabs(tv[2][2]);

	if (x >= y)
	{
	    if (x >= z)
	    {
#if (DEBUG_LEVEL & DEBUG_TRACE)
		fprintf(stderr, " X axis %g \n", tv[0][2]);
#endif
		best_axis = X_AXIS;
		dot = x;
		dot = -tv[0][2];
	    }
	    else
	    {
#if (DEBUG_LEVEL & DEBUG_TRACE)
		fprintf(stderr, " Z axis %g \n", tv[2][2]);
#endif
		best_axis = Z_AXIS;
		dot = z;
		dot = -tv[2][2];
	    }
	}
	else
	{
	    if (y >= z)
	    {
#if (DEBUG_LEVEL & DEBUG_TRACE)
		fprintf(stderr, " Y axis %g \n", tv[1][2]);
#endif
		best_axis = Y_AXIS;
		dot = y;
		dot = tv[1][2];
	    }
	    else
	    {
#if (DEBUG_LEVEL & DEBUG_TRACE)
		fprintf(stderr, " Z axis %g \n", tv[2][2]);
#endif
		best_axis = Z_AXIS;
		dot = z;
		dot = -tv[2][2];
	    }
	}
    }

    /* how big is the texture? */
    {
	if (s->texture->images[0] == NULL)
	    rmError(" rmOctmesh - big trouble, the texture doesn't have any image data!! Expect a segfault soon.");
	rmImageGetImageSize(s->texture->images[0], NULL, textureDims+0, textureDims+1, textureDims+2, NULL, NULL);
    }

    /* obtain the size of the octmesh */
    sblob = private_rmBlobFromIndex(p, BLOB_OMESHDIMS_INDEX);
    sstride = private_rmBlobGetStride(sblob) / sizeof(int);
    dims = (int *)private_rmBlobGetData(sblob);

    /* obtain the grid for the octmesh - we're assuming always a min/max grid (10/11/98) */
    vblob = private_rmBlobFromIndex(p, BLOB_OMESH_RECTGRID_INDEX);
    vstride = private_rmBlobGetStride(vblob) / sizeof(float);
    nverts = private_rmBlobGetNthings(vblob);
    v = (float *)private_rmBlobGetData(vblob);

    divisor = private_rmPrimitiveGetModelFlag(p) & RM_OCTMESH_DIVISOR_MASK;
    renderingMode = private_rmPrimitiveGetModelFlag(p) & ~RM_OCTMESH_DIVISOR_MASK;
    if (divisor == 0)		/* an RM error of some type */
    {
	rmError("rmOctmesh error: the model flag for the octmesh returned a zero.");
	divisor = 2;		/* pick something */
    }

    if ((renderingMode == RM_OCTMESH_2DTEXTURES_MIN_MEMORY) ||
	(renderingMode == RM_OCTMESH_2DTEXTURES_MAX_PERFORMANCE))
    {
	RMenum rebuildTextures = RM_FALSE;
	
	if (s->texture->cacheKeyData != p->utilCacheKey)
	{
	    rebuildTextures = RM_TRUE;
	    p->utilCacheKey = s->texture->cacheKeyData;
	}
	
	private_renderOctmeshUsing2DTextures(p, best_axis, dot, divisor, v, dims, textureDims, s->texture, rebuildTextures, renderingMode);
    }
    else 			/* assume 3D textures */
    {
	private_renderOctmeshUsing3DTexture(best_axis, dot, divisor, v, dims, textureDims);
    }
}
/* EOF */
