/* * Copyright (C) 2001-2003, 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: elevImage.c,v 1.7 2003/07/20 14:56:32 wes Exp $ * $Revision: 1.7 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: elevImage.c,v $ * Revision 1.7 2003/07/20 14:56:32 wes * Minor tweaks to fix compile warnings on Solaris. * * Revision 1.6 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.5 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.4 2003/01/16 22:22:45 wes * Updated all source files to reflect new organization of header files: all * headers that were formerly located in include/rmaux, include/rmv and * include/rmi are now located in include/rm. * * Revision 1.3 2002/06/20 13:11:38 wes * Fixed bug where crash occured on some platforms while reading * short data. * * Revision 1.2 2002/06/17 00:36:40 wes * Added code to do scanline padding for RM_SHORT pixels when needed. * This fixes a bug that showed up on Win32 where pixel data is * aligned to 4-byte boundaries per scanline. * * Revision 1.1 2001/11/03 16:11:53 wes * Initial entry. * */ /* * usage: elevImage [-w imgWidthPixels -h imgHeightPixels] [-f | -s (select either RM_FLOAT or RM_SHORT as the pixel type, default is RM_FLOAT) ] * * This code is a skeleton application template. Use it as the basis * for your own OpenRM applications. */ #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; static RMenum pixelType = RM_FLOAT; /*#define DEFAULT_IMAGE_WIDTH 1048 */ #define DEFAULT_IMAGE_WIDTH 980 #define DEFAULT_IMAGE_HEIGHT 525 static char dataFilename[]={"data/elevData.txt"}; static int dataWidth, dataHeight; static float dataMin, dataMax; static float *floatDataValues=NULL; static short *shortDataValues = NULL; int readData(char *fn, RMenum pixelType) { 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; if (pixelType == RM_FLOAT) { 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]; } } else /* assume RM_SHORT */ { int t; shortDataValues = (short *)malloc(sizeof(unsigned short)*n); for (i=0;i<n;i++) { fscanf(f,"%d",&t); shortDataValues[i] = t; if (shortDataValues[i] < dataMin) dataMin = shortDataValues[i]; if (shortDataValues[i] > dataMax) dataMax = shortDataValues[i]; } } printf(" done. min/max are %f, %f\n", dataMin, dataMax); dataWidth = w; dataHeight = h; return 1; } void make2DCamera(RMnode *n, int windowWidth, int windowHeight) { RMcamera2D *c = rmCamera2DNew(); rmCamera2DSetExtents(c, -1.01F, -1.01F, 1.01F, 1.01F); rmCamera2DSetAspectRatio(c, 1.0F); rmNodeSetSceneCamera2D(n, c); rmCamera2DDelete(c); } void makeFBClear(RMnode *n) { RMcolor4D bgColor={.3F, .3F, .3F, 1.0F}; rmNodeSetSceneBackgroundColor(n, &bgColor); } void makeGeoVismap(RMvisMap *m, float dataMin, float dataMax) { int zeroIndex; float t; int i, n; RMcolor3D black={0.1, 0.1, 0.1}; /* tmp; */ 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 makeImages(RMnode *n, RMpipe *pipe, RMenum pixelType) { RMprimitive *p; RMvertex2D v00={-1.0F, 0.0F}, v01={-1.0F, -1.0F}; RMvertex2D v10={0.0F, 0.0F}, v11={0.0F, -1.0F}; RMimage *baseImage=NULL, *resizedImage=NULL; RMimage *scaleBiasImage=NULL; RMimage *colorizedImage=NULL; RMimage *colorizedScaleBiasImage=NULL; float scaleValue, biasValue; RMvisMap *m; baseImage = rmImageNew(2,dataWidth, dataHeight,1, RM_IMAGE_LUMINANCE, pixelType, RM_COPY_DATA); resizedImage = rmImageNew(2, 512, 256, 1, RM_IMAGE_LUMINANCE, pixelType, RM_COPY_DATA); if (pixelType == RM_FLOAT) rmImageSetPixelData(baseImage, (void *)floatDataValues, RM_COPY_DATA,NULL); else { /* might have to tweak the raw RM_SHORT pixel data to honor scanline padding policy on the local OpenGL platform. */ short *newShortDataValues=NULL; int bps; bps = rmImageGetBytesPerScanline(baseImage); if (bps != sizeof(short)*dataWidth) { int i; newShortDataValues = (short *)malloc(sizeof(short)*bps * dataHeight); for (i=0;i<dataHeight;i++) memcpy((void *)(newShortDataValues + i*(bps/sizeof(short))), (void *)(shortDataValues + i*dataWidth), bps); } else newShortDataValues = shortDataValues; rmImageSetPixelData(baseImage, (void *)newShortDataValues, RM_COPY_DATA,NULL); if (bps != sizeof(short)*dataWidth) free((void *)newShortDataValues); } rmImageMirror(baseImage, RM_IMAGE_MIRROR_HEIGHT); rmImageResize(baseImage, resizedImage, RM_SOFTWARE, pipe); /* rmImageResize(baseImage, resizedImage, RM_HARDWARE, pipe); */ p = rmPrimitiveNew(RM_SPRITE); rmPrimitiveSetVertex2D(p, 1, &v00, RM_COPY_DATA, NULL); rmPrimitiveSetSprites(p, 1, &resizedImage); rmNodeAddPrimitive(n, p); scaleBiasImage = rmImageDup(resizedImage); if (pixelType == RM_FLOAT) { /* scale image from dataMin..dataMax to 0..1 */ scaleValue = 1.0F/(dataMax - dataMin); /* bias should be in range 0..1. what we want is for data values equal to dataMin to map to 0.0 in pixel values */ biasValue = -dataMin * scaleValue; } else /* assume RM_SHORT */ { /* scale pixels into range 0..32768 */ scaleValue = 32768.0F/(dataMax-dataMin); biasValue = -dataMin * scaleValue/32768.0F; } rmImageSetScale(scaleBiasImage, scaleValue); rmImageSetBias(scaleBiasImage, biasValue); p = rmPrimitiveNew(RM_SPRITE); rmPrimitiveSetVertex2D(p, 1, &v01, RM_COPY_DATA, NULL); rmPrimitiveSetSprites(p, 1, &scaleBiasImage); rmNodeAddPrimitive(n, p); colorizedImage = rmImageDup(resizedImage); m = rmDefaultVismap(); makeGeoVismap(m, dataMin, dataMax); rmImageSetVismap(colorizedImage,m); p = rmPrimitiveNew(RM_SPRITE); rmPrimitiveSetVertex2D(p, 1, &v10, RM_COPY_DATA, NULL); rmPrimitiveSetSprites(p, 1, &colorizedImage); rmNodeAddPrimitive(n, p); colorizedScaleBiasImage = rmImageDup(scaleBiasImage); rmImageSetVismap(colorizedScaleBiasImage,m); p = rmPrimitiveNew(RM_SPRITE); rmPrimitiveSetVertex2D(p, 1, &v11, RM_COPY_DATA, NULL); rmPrimitiveSetSprites(p, 1, &colorizedScaleBiasImage); rmNodeAddPrimitive(n, p); } void myInitFunc(RMpipe *p, RMnode *n) { RMnode *localRoot = rmNodeNew("localRoot", RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE); int w,h; if (readData(dataFilename, pixelType) == 0) { rmError(" myInitFunc() error: unable to open source data. Bye!"); exit(-1); } makeImages(localRoot,p, pixelType); rmPipeGetWindowSize(p, &w, &h); make2DCamera(localRoot, w, h); makeFBClear(localRoot); rmNodeAddChild(n, localRoot); rmFrame(p, n); } void myRenderFunc(RMpipe *myPipe, RMnode *subTree) { /* insert code to call frame-based renderer here */ rmFrame(myPipe, subTree); } void usage(char *s) { printf("usage: %s [-w imgWidthPixels ] [-h imgHeightPixels] [-f | -s (selects either RM_FLOAT or RM_SHORT as the pixel type, default is RM_FLOAT) ]\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++; ac--; sscanf(av[i],"%d", imgWidth); } else if (strcmp(av[i],"-h") == 0) { i++; ac--; sscanf(av[i],"%d", imgHeight); } else if (strcmp(av[i],"-f") == 0) pixelType = RM_FLOAT; else if (strcmp(av[i],"-s") == 0) pixelType = RM_SHORT; 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); #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); /* * set key handler function so this prog will exit on "q" key. */ rmauxSetKeyFunc(myPipe, rmauxDefaultKeyFunc); rmauxEventLoop(myPipe,rmRootNode(), &msg); rmFinish(); return(1); }