/* * Copyright (C) 1997-2005, 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 * 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: OpenRM-1-6-0-RC5 $ * $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 #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; yimages[0]->bytes_per_component * t->images[0]->elements; for (z=0;zimages[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;zimages[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 */