/* * Copyright (C) 2000-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/. */ /* * demonstration of multiple views (viewports) within a single window */ /* * $Id: elev-2vu.c,v 1.8 2003/11/05 15:53:18 wes Exp $ * $Revision: 1.8 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: elev-2vu.c,v $ * Revision 1.8 2003/11/05 15:53:18 wes * Added parameters to modified 1.5.1 rmv routines that specify whether * or not to flip surface normals. * * Revision 1.7 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.6 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.5 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.4 2002/06/17 00:35:40 wes * Updated copyright line. * * Revision 1.3 2001/07/15 22:33:19 wes * Added rmPipeDelete to the end of all demo progs. For those that use * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen). * * Revision 1.2 2001/06/03 19:44:05 wes * Add calls to new rmaux routines to handle window resize events, and * for keyboard event handling. * * Revision 1.1 2001/03/31 17:26:32 wes * Initial entry. * */ #include <rm/rm.h> #include <rm/rmaux.h> #include <rm/rmv.h> #include "libdio.h" #include "procmode.h" static char MyRootName[]={"MyRoot"}; static RMnode *MyRoot, *miniMyRoot; int img_width=400,img_height=300; char datafilename[256]={"data/elev.dio"}; dioDataObject *mydataobj=NULL; dioDataObject *newdataobj=NULL; int doColor=0; void usage(char *av[]) { char buf[256]; sprintf(buf," usage: %s [-i datafilename (defaults to data/elev.dio) [-w img_width] [-h img_height] [-c (use default vis colormap to 'colorize' the plot') \n",av[0]); rmError(buf); } void parse_args(int ac, char *av[]) { int i; i = 1; while (i < ac) { if (strcmp(av[i],"-w") == 0) { i++; sscanf(av[i],"%d",&img_width); } else if (strcmp(av[i],"-h") == 0) { i++; sscanf(av[i],"%d",&img_height); } else if (strcmp(av[i],"-i") == 0) { i++; strcpy(datafilename,av[i]); } else if (strcmp(av[i],"-c") == 0) { doColor=1; } else { usage(av); exit(-1); } i++; } } void my_read_data(char *datafilename) { float scalef=4.0; mydataobj = dioReadDataObject(datafilename); if (mydataobj == NULL) { fprintf(stderr," error reading input data file. exiting. \n"); exit(-1); } dioObjectScaleData(mydataobj, scalef); dioObjectConditioner(mydataobj); } void my_set_scene(RMnode *camNode, int channelFormat) { /* * here, we compute the camera parameters such that all of * the geometry is visible. */ RMcamera3D *c=rmCamera3DNew(); rmDefaultCamera3D(c); /* assign it some default values. */ rmCamera3DComputeViewFromGeometry(c,MyRoot, img_width, img_height); if (channelFormat != RM_MONO_CHANNEL) { rmCamera3DSetStereo(c,RM_TRUE); rmCamera3DSetEyeSeparation(c,2.5F); rmCamera3DSetFocalDistance (c,0.707F); } /* add the camera to "my root's" scene parms. */ rmNodeSetSceneCamera3D(camNode,c); rmCamera3DDelete(c); /* use RM's default lighting model */ rmDefaultLighting(camNode); } RMvertex3D mygridfunc_uv(int i, int j) { /* * tell RMV what this grid (x,y,z) point is at location (i,j). * we assume the data model is sufficiently intelligent to know * it's own dimensions, and is capable of dealing with a two-dimensional * indexing system. * * we assume that the "i" index maps to width, and that "j" * maps to height in the local data model. */ RMvertex3D temp3d; int indx; /* indx = mydataobj->width * j + i; */ indx = newdataobj->dims[0] * j + i; temp3d.x = newdataobj->xcoords[indx]; temp3d.y = newdataobj->ycoords[indx]; temp3d.z = newdataobj->zcoords[indx]; return(temp3d); } RMvertex3D mypoints_gridfunc(int i, int j) { /* * this routine shows how to "trick" rmvJ3ScatterPoints - it steps * through 2 logical grid dimensions, but all we really have is just * one. this routine is the interface to the local data model..which * fakes a 2d grid with 1d unstructured grid. */ RMvertex3D temp3d; temp3d.x = mydataobj->xcoords[i]; temp3d.y = mydataobj->ycoords[i]; temp3d.z = mydataobj->rawdata[i]; /* heh heh */ return(temp3d); } float mypoints_datafunc(int i, int j) { /* * this routine shows how to "trick" rmvJ3ScatterPoints - it steps * through 2 logical grid dimensions, but all we really have is just * one. this routine is the interface to the local data model..which * fakes a 2d grid with 1d unstructured grid. */ return(mydataobj->rawdata[i]); } float mydatafunc_uv(int i, int j) { /* * tell RMV what the data value is at grid location (i,j). we * assume a one-d grid. */ int indx; indx = newdataobj->dims[0] * j + i; return(newdataobj->rawdata[indx]); } void my_build_objs(void) { MyRoot = rmNodeNew(MyRootName,RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); miniMyRoot = rmNodeNew("MiniME", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); rmNodeAddChild(rmRootNode(),MyRoot); rmNodeAddChild(rmRootNode(),miniMyRoot); /* do the visualization.. */ { RMnode *visnode; float *xcoords,*ycoords,*zcoords, *data,*data2; RMvisMap *vmap=NULL; float *newdata; int new_usize=30, new_vsize=30; newdata = (float *)malloc(sizeof(float)*new_usize*new_vsize); /* * note that we're making certain assumptions about the * input data file: * 1. it has xcoords & ycoords * 2. we treat the "raw data" as z coords, * 3. effectively ignoring any z coords that may be present in the file */ xcoords = mydataobj->xcoords; ycoords = mydataobj->ycoords; zcoords = mydataobj->rawdata; data = mydataobj->rawdata; if (doColor) { vmap = rmDefaultVismap(); rmVismapSetTfMin(vmap,mydataobj->datamin); rmVismapSetTfMax(vmap,mydataobj->datamax); data2 = mydataobj->rawdata; } else { data2 = NULL; vmap = NULL; } visnode = rmNodeNew("vis",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); /* show the raw data as point primitives */ { rmvJ3ScatterPoint(mypoints_gridfunc, mypoints_datafunc, (vmap == NULL) ? NULL : mypoints_datafunc, vmap, RMV_NOAXIS_OFFSET, mydataobj->width,1, RM_TRUE, RM_FALSE, visnode); rmNodeSetPointSize(visnode, 2.0F); /* make them fat points */ } /* do the surface fitting */ { RMvertex2D omin, omax; int method; float float_parm; /* * the surface fit routine will compute a surface over a grid * of resolution (new_usize, new_vsize), the corners of which * are located at omin,omax (those are x,y points). we're asking * the surface fitter to compute a surface over the entire * spatial range of data. */ omin.x = mydataobj->corners[0].x; omax.x = mydataobj->corners[1].x; omin.y = mydataobj->corners[0].y; omax.y = mydataobj->corners[1].y; /* * uncomment this next section to try out RMV_GRID2D_IDSFFT * surface fitting. if you do, be sure to comment the other * section of code that does RMV_GRID2D_KRIGE fitting. */ #if 0 method = RMV_GRID2D_IDSFFT; /* floatparm = number of additional data points used for esti- */ /* mating partial derivatives at each data point */ /* (must be 2 or greater, but smaller than the number of total data points (internal restrictions limit the max for this parameter to 25), */ /* recommended values for this parm are between 3-5 */ float_parm = 3; #endif method = RMV_GRID2D_KRIGE; float_parm = 2.0; rmv2DSurfaceFit(xcoords, ycoords, zcoords, mydataobj->width, &omin, &omax, new_usize, new_vsize, method, float_parm, newdata); } /* * now, configure the new data object for visualization using the * results of the surface fitting operation. */ newdataobj = dioDataObjectNew(); newdataobj->width = new_usize; newdataobj->height = new_vsize; newdataobj->depth = 1; newdataobj->corners = rmVertex3DNew(2); newdataobj->corners[0] = mydataobj->corners[0]; newdataobj->corners[1] = mydataobj->corners[1]; newdataobj->corners[0].z = 0.0; newdataobj->corners[1].z = 0.0; newdataobj->rawdata = newdata; dioObjectConditioner(newdataobj); rmvJ3MeshSurface(mygridfunc_uv, mydatafunc_uv, (vmap == NULL) ? NULL : mydatafunc_uv, vmap, RMV_ZAXIS_OFFSET, new_usize, new_vsize, RM_FALSE, visnode); rmNodeAddChild(MyRoot,visnode); rmNodeAddChild(miniMyRoot, visnode); rmNodeComputeBoundingBox(visnode); } rmNodeUnionAllBoxes(MyRoot); rmNodeUnionAllBoxes(miniMyRoot); { RMvertex3D bmin,bmax,center; rmNodeGetBoundingBox(MyRoot,&bmin,&bmax); center.x = bmin.x + 0.5 * (bmax.x - bmin.x); center.y = bmin.y + 0.5 * (bmax.y - bmin.y); center.z = bmin.z + 0.5 * (bmax.z - bmin.z); rmNodeSetCenter(MyRoot,¢er); rmNodeSetCenter(miniMyRoot,¢er); } { RMcolor4D bgcolor={0.0,0.0,0.0,0.0}; float vp[4] = {0.0, 0.0, 1.0, 1.0}; /* * assign a background color to take effect at "MyRoot" */ rmNodeSetSceneBackgroundColor(MyRoot,&bgcolor); rmNodeSetSceneViewport(MyRoot, vp); } { RMcolor4D bgcolor={0.2,0.2,0.3,0.0}; float vp[4] = {0.75, 0.8, 1.0, 1.0}; /* * assign a background color to take effect at "MyRoot" */ rmNodeSetSceneBackgroundColor(miniMyRoot,&bgcolor); rmNodeSetSceneViewport(miniMyRoot, vp); } } void my_idle_func(RMpipe *p, int ix, int iy) { RMmatrix m,old; double d,c,s; rmMatrixIdentity(&m); d = RM_DEGREES_TO_RADIANS(1.0); c = cos(d); s = sin(d); m.m[0][0] = m.m[2][2] = c; m.m[0][2] = -s; m.m[2][0] = s; if (rmNodeGetRotateMatrix(MyRoot,&old) == RM_WHACKED) rmMatrixIdentity(&old); rmMatrixMultiply(&old,&m,&old); rmNodeSetRotateMatrix(MyRoot,&old); rmFrame(p, rmRootNode()); } void myinitfunc(RMpipe *p, RMnode *n) { my_read_data(datafilename); my_build_objs(); my_set_scene(rmRootNode(), rmPipeGetChannelFormat(p)); /* * set up the event handler to apply geometric transformations at * MyRoot. note that the lights and cameras are placed at rmRootNode(). * therefore, the rotations & scaling applied at MyRoot do not affect * the cameras & lights since they are at a higher level in the * scene graph than MyRoot. */ rmauxSetGeomTransform(MyRoot,p); rmauxSetCamera3DTransform(rmRootNode(), p); /* * set handler to reset aspect ratio when the window is resized. */ if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE) rmFrame(p, rmRootNode()); rmFrame(p, rmRootNode()); } void myrenderfunc(RMpipe *p, RMnode *n) { #if 0 glDisable(GL_SCISSOR_TEST); glViewport(0,0,img_width, img_height); glClearColor(0.3F, 0.1F, 0.1F, 1.0F); glClear(GL_COLOR_BUFFER_BIT); glViewport(0,0,img_width/4, img_height/4); glScissor(0,0,img_width/4, img_height/4); glEnable(GL_SCISSOR_TEST); glClearColor(0.1F, 0.1F, 0.3F, 1.0F); glClear(GL_COLOR_BUFFER_BIT); private_postRenderSwapBuffersFunc(p); #endif rmFrame(p, rmRootNode()); #if 0 private_rmView(lone_pipe, rmRootNode()); private_rmRender(lone_pipe); #endif } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { RMpipe *myPipe=NULL; MSG msg; HWND hWnd; void *fptr; int status; RMenum channelFormat; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_WGL; parse_args(__argc,__argv); #else int main(int ac, char *av[]) { RMpipe *myPipe=NULL; int status; RMenum channelFormat; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_GLX; void *msg; /* needed for rmauxEventLoop win32/unix API consistency */ parse_args(ac,av); #endif /* * pick a stereo format: * RM_MONO_CHANNEL - plain old single-view * RM_REDBLUE_STEREO_CHANNEL - left channel in red, right channel in cyan * RM_BLUERED_STEREO_CHANNEL - left in cyan, right in red * RM_MBUF_STEREO_CHANNEL - multibuffered stereo, requires special * hardware. */ channelFormat = RM_MONO_CHANNEL; /* * first stage of RM initialization. */ rmInit(); /* * create the rendering pipe. this step is required in both * Win32 and X. */ myPipe = rmPipeNew(targetPlatform); 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,img_width,img_height,"RM for Windows", hInstance,fptr); if (hWnd == 0) exit(-1); /* * assign the new window handle to the rendering pipe. */ rmPipeSetWindow(myPipe,hWnd, img_width, img_height); /* uncomment this next line if you want the object to rotate while the user is idle. */ /* rmauxSetIdleFunc(my_idle_func, hWnd); */ } #endif #ifdef RM_X { Window w; w = rmauxCreateXWindow(myPipe, (Window)NULL, /* parent window */ 0,0,img_width,img_height, "RM for X-Windows","icon-title", RM_TRUE); /* * assign the window to the rendering pipe. */ rmPipeSetWindow(myPipe,w,img_width,img_height); } #endif /* * specify the name of the "init" function. the "init" function is * mandatory in the Win32 world, and optional in the X world. * * in Win32, we don't want to call RM services until OpenGL is * ready. we can be assured of readiness by using an init function * with RMaux. * * in X, at this point, the window is mapped and OpenGL is ready, * and we could call our init function directly. */ rmauxSetInitFunc(myinitfunc); /* * 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); rmauxSetRenderFunc(myrenderfunc); /* * set key handler function so this prog will exit on "q" key */ rmauxSetKeyFunc(myPipe, rmauxDefaultKeyFunc); rmauxEventLoop(myPipe,rmRootNode(), &msg); rmPipeDelete(myPipe); rmFinish(); #ifdef RM_WIN return( msg.wParam ); #else return(1); #endif }