/* * Copyright (C) 2004, R3vis Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; 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/gpl.html. * * Contributor(s): * Wes Bethel, R3vis Corporation, Marin County, California * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: indexedPrims2D.c,v 1.2 2004/03/10 01:50:53 wes Exp $ * $Revision: 1.2 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: indexedPrims2D.c,v $ * Revision 1.2 2004/03/10 01:50:53 wes * Added routine to generate an RM_INDEXED_QUAD_STRIP primitive, and * some minor index reordering to produce more reasonable primitives. * * Revision 1.1 2004/02/23 02:53:08 wes * Initial entry. * */ /* * usage: indexedPrims2D [-w imgWidthPixels ] [-h imgHeightPixels ] [ -prim ( iquads | itstrips | itris | iqstrips (default is iquads)) ] [-draw ( fill | line | point (default is fill)) ] */ #include <stdio.h> #ifndef RM_WIN #include <unistd.h> #endif #include <rm/rm.h> #include <rm/rmaux.h> #include "procmode.h" static RMpipe *myPipe=NULL; #define DEFAULT_IMAGE_WIDTH 900 #define DEFAULT_IMAGE_HEIGHT 480 static float *floatDataValues=NULL; static RMcolor3D *colorValues=NULL; static float dataMin, dataMax; static int dataWidth, dataHeight; static RMvertex2D *baseVertices=NULL; static RMnode *myRoot=NULL, *updateNode=NULL; static RMprimitive *updatePrimitive; static int *indices=NULL; static RMenum indexedPrimType = RM_INDEXED_QUADS; static RMenum drawMode = RM_FILL; /* * the following are the lat/lon values associated with the elev data. * for details, see: www.openrm.org/docs/elevImage-NOTES.txt */ RMvertex2D coordMin={-134.0, -51.0}, coordMax={-66.0, -21.0}; int readData(char *fn) { FILE *f = fopen(fn,"r"); int w,h,n,i; if (f == NULL) return(0); fscanf(f,"%d %d",&w,&h); printf(" data width/height are %d, %d \n", w,h); n = w*h; printf(" reading data ..."); dataMin = 1.0e10; dataMax = -dataMin; floatDataValues = (float *)malloc(sizeof(float)*n); for (i=0;i<n;i++) { fscanf(f,"%f",floatDataValues+i); if (floatDataValues[i] < dataMin) dataMin = floatDataValues[i]; if (floatDataValues[i] > dataMax) dataMax = floatDataValues[i]; } printf(" done. min/max are %f, %f\n", dataMin, dataMax); dataWidth = w; dataHeight = h; return 1; } void makeGeoVismap(RMvisMap *m, float dataMin, float dataMax) { int zeroIndex; float t; int i, n; RMcolor3D terrainStartHSV = {0.33, 0.9, 0.9}; RMcolor3D terrainEndHSV = {0.0, 0.9, 0.9}; RMcolor3D waterStartHSV = {0.475, 0.9, 0.9}; RMcolor3D waterEndHSV = {0.60, 0.9, 0.9}; float dh, ds, dv, h, s, v; /* objective: create a colormap such that negative values (below sea level) are shades of blue, and positive values range from greens through yellows to red (hue ramp). */ /* find index in range 0..255 that corresponds to zero elevation */ t = 255.0F * (-dataMin/(dataMax - dataMin)); zeroIndex = (int)t; zeroIndex += 1; n = 255 - zeroIndex + 1; /* do below sea level values */ dh = (waterEndHSV.r - waterStartHSV.r)/ (float)(n); ds = (waterEndHSV.g - waterStartHSV.g)/ (float)(n); dv = (waterEndHSV.b - waterStartHSV.b)/ (float)(n); h = waterStartHSV.r; s = waterStartHSV.g; v = waterStartHSV.b; for (i=zeroIndex-1; i >= 0; i--) { RMcolor3D c; rmHSVtoRGB(h, s, v, &c.r, &c.g, &c.b); rmVismapSetColor3D(m, i, &c); h += dh; s += ds; v += dv; } /* do above sea level values */ dh = (terrainEndHSV.r - terrainStartHSV.r)/ (float)(n); ds = (terrainEndHSV.g - terrainStartHSV.g)/ (float)(n); dv = (terrainEndHSV.b - terrainStartHSV.b)/ (float)(n); h = terrainStartHSV.r; s = terrainStartHSV.g; v = terrainStartHSV.b; for (i=zeroIndex; i<255; i++) { RMcolor3D c; rmHSVtoRGB(h, s, v, &c.r, &c.g, &c.b); rmVismapSetColor3D(m, i, &c); h += dh; s += ds; v += dv; } } void createColors(int dWidth, int dHeight, float dMin, float dMax, float *f, RMcolor3D **returnColors) { int i; RMvisMap *m = rmVismapNew(256); RMcolor3D *c; makeGeoVismap(m, dMin,dMax); rmVismapSetTfMin(m, dMin); rmVismapSetTfMax(m, dMax); *returnColors = c = rmColor3DNew(dWidth*dHeight); for (i=0;i<dWidth*dHeight;i++) { int indx; indx = rmVismapIndexFromData(m, f[i]); rmVismapGetColor3D(m, indx, c+i); } } void createBaseVertices(const RMvertex2D *coordMin, const RMvertex2D *coordMax, int dWidth, int dHeight, float *d, RMvertex2D **baseVertices) { /* * compute a set of vertices that contain the coordinates for the * entire wad of data. The min/max coordinates, or grid corners, are * defined by constants at the top of this file, and correspond to * lat/lon values. We are basically going to interpolate between the * grid corners to construct a full resolution grid. */ float x, dx; float y, dy; int i,j, indx=0 ; RMvertex2D *v = rmVertex2DNew(dWidth*dHeight); *baseVertices = v; dx = (coordMax->x - coordMin->x)/(float)(dWidth-1); dy = (coordMax->y - coordMin->y)/(float)(dHeight-1); y = coordMin->y; for (j=0;j<dHeight;j++, y+=dy) { x = coordMin->x; for (i=0;i<dWidth;i++, x+=dx, indx++) { v[indx].x = x; v[indx].y = y; } } } void myFreeFunc(void *d) { /* * this is a no-op function as it is called on each frame since we * assign, then update, the primitive indices. */ } void createBaseSceneGraph(RMnode **myRoot, RMnode **updateNode, RMvertex2D *gMin, RMvertex2D *gMax, RMvertex2D *baseVertices, RMcolor3D *colorValues, int dWidth, int dHeight) { RMnode *n, *u, *n2; RMcamera2D *camera = rmCamera2DNew(); RMcolor4D bgColor = {0.2, 0.2, 0.2, 1.0}; RMprimitive *outlineBox; RMvertex2D extents[2]; *myRoot = n = rmNodeNew("myRoot", RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE); *updateNode = u = rmNodeNew("updateNode", RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE); rmNodeAddChild(rmRootNode(), n); rmNodeSetSceneBackgroundColor(n, &bgColor); { extents[0].x = gMin->x - (gMax->x - gMin->x)*0.1; extents[0].y = gMin->y - (gMax->y - gMin->y)*0.1; extents[1].x = gMax->x + (gMax->x - gMin->x)*0.1; extents[1].y = gMax->y + (gMax->y - gMin->y)*0.1; rmCamera2DSetExtents(camera, extents[0].x, extents[1].y, extents[1].x, extents[0].y); } rmNodeSetSceneCamera2D(n, camera); rmNodeAddChild(n, u); { RMvertex2D v[2]; v[0] = *gMin; v[1] = *gMax; outlineBox = rmPrimitiveNew(RM_BOX2D); rmPrimitiveSetVertex2D(outlineBox, 2, v, RM_COPY_DATA, NULL); n2 = rmNodeNew("staticBox", RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE); rmNodeAddPrimitive(n2, outlineBox); rmNodeSetPolygonDrawMode(n2, RM_FRONT_AND_BACK, RM_LINE); rmNodeAddChild(n, n2); } updatePrimitive = rmPrimitiveNew(indexedPrimType); rmNodeAddPrimitive(u, updatePrimitive); rmNodeSetPolygonDrawMode(u, RM_FRONT_AND_BACK, drawMode); rmPrimitiveSetVertex2D(updatePrimitive, dWidth*dHeight, baseVertices, RM_DONT_COPY_DATA, myFreeFunc); rmPrimitiveSetColor3D(updatePrimitive, dWidth*dHeight, colorValues, RM_DONT_COPY_DATA, myFreeFunc); } void createBaseIndices(int dWidth, int dHeight, RMenum indexedPrimType, int **indices) { /* * size of index pool is a function of dWidth, not dHeight. See * use of frameNumber % dWidth in updateSceneGraph(). */ int count; switch (indexedPrimType) { case RM_INDEXED_QUADS: count = (dWidth+1)*(dWidth+1)*4; break; case RM_INDEXED_TRIANGLES: count = (dWidth+1)*(dWidth+1)*6; break; case RM_INDEXED_QUAD_STRIP: case RM_INDEXED_TRIANGLE_STRIP: count = ((dWidth+1)*2+4)*(dWidth+1); /* extra points to create degenerate prims */ break; default: rmWarning(" createBaseIndices() error - unable to compute the number of indices needed for the requested primitive type. \n"); count = 1; break; } *indices = (int *)malloc(sizeof(int)*count); } void myInitFunc(RMpipe *p, RMnode *n) { /* read in the data we'll be working with */ readData("data/elevData.txt"); /* create a big array of RGB values for each data point */ createColors(dataWidth, dataHeight, dataMin, dataMax, floatDataValues, &colorValues); /* compute a set of x/y vertices for each possibled grid point */ createBaseVertices(&coordMin, &coordMax, dataWidth, dataHeight, floatDataValues, &baseVertices); /* malloc some memory for indices */ createBaseIndices(dataWidth, dataHeight, indexedPrimType, &indices); /* generate the base-level scene graph. */ createBaseSceneGraph(&myRoot, &updateNode, &coordMin, &coordMax, baseVertices, colorValues, dataWidth, dataHeight); rmFrame(p, n); } #define makeIndex(i,j,w) ((j)*(w)+(i)) void generateIndices(int frameNumber) { int indexCount; int *ii = indices; int indx; int nDivisions = frameNumber % dataWidth; int i,j; int *iDivs, *jDivs; float d,dDiv; iDivs = (int *)malloc(sizeof(int)*(nDivisions+2)); jDivs = (int *)malloc(sizeof(int)*(nDivisions+2)); d = dDiv = (float)dataWidth/(float)(nDivisions+1); iDivs[0] = 0; for (i=1;i<=nDivisions;i++,d+=dDiv) iDivs[i] = (int)d; iDivs[i] = dataWidth-1; d = dDiv = (float)dataHeight/(float)(nDivisions+1); jDivs[0] = 0; for (i=1;i<=nDivisions;i++,d+=dDiv) jDivs[i] = (int)d; jDivs[i] = dataHeight-1; indx = 0; switch (indexedPrimType) { case RM_INDEXED_QUADS: { indexCount = (nDivisions+1)*(nDivisions+1)*4; for (j=0;j<nDivisions+1;j++) { for (i=0;i<nDivisions+1;i++) { ii[indx++] = makeIndex(iDivs[i], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i+1], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i+1], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); } } } break; case RM_INDEXED_TRIANGLES: { indexCount = (nDivisions+1)*(nDivisions+1)*6; for (j=0;j<nDivisions+1;j++) { for (i=0;i<nDivisions+1;i++) { ii[indx++] = makeIndex(iDivs[i], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i+1], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[i+1], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i+1], jDivs[j+1], dataWidth); } } } break; case RM_INDEXED_TRIANGLE_STRIP: { indexCount = ((nDivisions+1)*2+4) * (nDivisions+1); for (j=0;j<nDivisions+1;j++) { for (i=0;i<nDivisions+1;i++) { ii[indx++] = makeIndex(iDivs[i], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); } ii[indx++] = makeIndex(iDivs[i], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[0], jDivs[j+1], dataWidth); } } break; case RM_INDEXED_QUAD_STRIP: { indexCount = ((nDivisions+1)*2+4) * (nDivisions+1); for (j=0;j<nDivisions+1;j++) { for (i=0;i<nDivisions+1;i++) { ii[indx++] = makeIndex(iDivs[i], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); } ii[indx++] = makeIndex(iDivs[i], jDivs[j], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[i], jDivs[j+1], dataWidth); ii[indx++] = makeIndex(iDivs[0], jDivs[j+1], dataWidth); } } break; } free((void *)iDivs); free((void *)jDivs); rmPrimitiveSetIndices(updatePrimitive, indexCount, indices, RM_DONT_COPY_DATA, myFreeFunc); } void myRenderFunc(RMpipe *myPipe, RMnode *subTree) { /* insert code to call frame-based renderer here */ int frameNumber; rmFrame(myPipe, subTree); frameNumber = rmPipeGetFrameNumber(myPipe); fprintf(stderr," render frame number = %d \n", frameNumber); } int myIdleFunc(RMpipe *p, int px, int py) { int frameNumber = rmPipeGetFrameNumber(p); fprintf(stderr," idle frame number = %d \n", frameNumber); generateIndices(frameNumber); rmFrame(p, rmRootNode()); return 1; } void usage(char *s) { fprintf(stderr,"usage: %s [-w imgWidthPixels ] [-h imgHeightPixels ] [ -prim ( iquads | itstrips | itris | iqstrips (default is iquads)) ] [-draw ( fill | line | point (default is fill)) ] \n",s); } void parseArgs(int ac, char *av[], int *imgWidth, int *imgHeight) { int i; i = 1; while (i < ac) { if (strcmp(av[i],"-w") == 0) { i++; sscanf(av[i],"%d", imgWidth); } else if (strcmp(av[i],"-h") == 0) { i++; sscanf(av[i],"%d", imgHeight); } else if (strcmp(av[i],"-prim") == 0) { i++; if (strcmp(av[i],"iquads") == 0) indexedPrimType = RM_INDEXED_QUADS; else if (strcmp(av[i],"itstrips") == 0) indexedPrimType = RM_INDEXED_TRIANGLE_STRIP; else if (strcmp(av[i],"itris") == 0) indexedPrimType = RM_INDEXED_TRIANGLES; else if (strcmp(av[i],"iqstrips") == 0) indexedPrimType = RM_INDEXED_QUAD_STRIP; else { fprintf(stderr," Bogus primitive type. \n"); usage(av[0]); exit(-1); } } else if (strcmp(av[i],"-draw") == 0) { i++; if (strcmp(av[i],"fill") == 0) drawMode = RM_FILL; else if (strcmp(av[i],"line") == 0) drawMode = RM_LINE; else if (strcmp(av[i],"point") == 0) drawMode = RM_POINT; else { fprintf(stderr," Bogus draw type. \n"); usage(av[0]); exit(-1); } } else { usage(av[0]); exit(-1); } i++; } } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND hWnd; void *fptr; int status; int imgWidth, imgHeight; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_WGL; imgWidth = DEFAULT_IMAGE_WIDTH; imgHeight = DEFAULT_IMAGE_HEIGHT; parseArgs(__argc, __argv, &imgWidth, &imgHeight); #else /* assume RM_X */ int main(int argc, char *argv[]) { void *msg; /* needed for rmauxEventLoop win32/unix API consistency */ int status; int imgWidth, imgHeight; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_GLX; imgWidth = DEFAULT_IMAGE_WIDTH; imgHeight = DEFAULT_IMAGE_HEIGHT; parseArgs(argc, argv, &imgWidth, &imgHeight); #endif /* * first stage of RM initialization. */ rmInit(); /* * create the rendering pipe. this step is required in both * Win32 and X. */ myPipe = rmPipeNew(targetPlatform); processingMode = RM_PIPE_MULTISTAGE; rmPipeSetProcessingMode(myPipe, processingMode); rmPipeSetDisplayListEnable(myPipe, RM_FALSE); rmPipeSetFrameRate(myPipe, 20); #ifdef RM_WIN { /* * Win32: when a window is created, we have to tell windows the * name of the "WndProc," the procedure that gets called by * windows with events (the event loop) (contrast to the X model * where the name of the event loop is not part of the window). * Since we're using RMaux, we know about the event handling * procedure named "rmauxWndProc" and we provide that here. */ fptr = (void *)(rmauxWndProc); hWnd = rmauxCreateW32Window(myPipe, NULL, /* no parent window */ 20,20,imgWidth,imgHeight,"RM for Windows", hInstance,fptr); if (hWnd == 0) exit(-1); /* * assign the new window handle to the rendering pipe. */ rmPipeSetWindow(myPipe,hWnd, imgWidth, imgHeight); } #endif #ifdef RM_X { Window w; w = rmauxCreateXWindow(myPipe, (Window)NULL, /* parent window */ 0,0,imgWidth,imgHeight, "RM for X-Windows","RM for X-Windows",RM_TRUE); /* * assign the window to the rendering pipe. */ rmPipeSetWindow(myPipe,w,imgWidth,imgHeight); } #endif /* * X-ism: once the window is created and assigned to the * rendering pipe, rmPipeMakeCurrent makes the OpenGL rendering context * current for the pipe+window combination. * * this step is required for X. in these demo programs, it is not * strictly required by Win32, as the newly created context is made * current as part of the OpenGL initialization sequence. */ rmPipeMakeCurrent(myPipe); rmauxSetInitFunc(myInitFunc); rmauxSetRenderFunc(myRenderFunc); rmauxSetIdleFunc(myPipe, myIdleFunc); /* * set key handler function so this prog will exit on "q" key. */ rmauxSetKeyFunc(myPipe, rmauxDefaultKeyFunc); rmauxEventLoop(myPipe, rmRootNode(), &msg); rmPipeDelete(myPipe); rmFinish(); return(1); }