/* * Copyright (C) 1997-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: orderTest.c,v 1.5 2004/01/17 03:17:32 wes Exp $ * $Revision: 1.5 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: orderTest.c,v $ * Revision 1.5 2004/01/17 03:17:32 wes * Removed C++ style comment delimeters, which work on on all versions of * gcc, and replaced them with C style comment delimeters so that the * SGI compiler will work. * * Revision 1.4 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.3 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.2 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.1 2002/08/29 22:22:06 wes * Initial entry. * */ #include <rm/rm.h> #include <rm/rmaux.h> #include <time.h> #include "procmode.h" /* * usage: orderTest [-w imgWidth] [-h imgHeight] [-n numPoints (default=100)] [-s (display framerate stats)] */ static char MyRootName[]={"MyRoot"}; static RMnode *MyRoot; static int img_width=400,img_height=300; static int rand_seed=314159; static int num_points=200; static int do_pointsize=1; static float pointsize=20.0; RMenum static_stereo_format; int doStats=0; int doOrder = 1; static int *staticIndices = NULL; static RMvertex3D *staticVerts = NULL; void usage(char *av[]) { char buf[256]; sprintf(buf," usage: %s [-w img_width] [-h img_height] [-n num_points] [-p size (point size in pixels) ] [-noOrder (turns off per-frame node sorting)] [-s (turn on compute/report of frame rate stats)]\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],"-noOrder") == 0) doOrder = 0; else if (strcmp(av[i],"-h") == 0) { i++; sscanf(av[i],"%d",&img_height); } else if (strcmp(av[i],"-s") == 0) { i++; doStats = 1; } else if (strcmp(av[i],"-n") == 0) { i++; sscanf(av[i],"%d",&num_points); } else if (strcmp(av[i],"-p") == 0) { i++; do_pointsize = 1; sscanf(av[i],"%f",&pointsize); } else { usage(av); exit(-1); } i++; } } void my_set_scene(RMnode *camNode, RMenum stereo_format) { /* * here, we compute the camera parameters such that all of * the geometry is visible. */ RMcamera3D *c=rmCamera3DNew(); /* assign a default view */ rmDefaultCamera3D(c); /* adjust the default view so that all geom is visible */ rmCamera3DComputeViewFromGeometry(c,MyRoot,img_width,img_height); if (stereo_format != RM_MONO_CHANNEL) { rmCamera3DSetStereo(c,RM_TRUE); rmCamera3DSetEyeSeparation(c,2.5F); rmCamera3DSetFocalDistance (c,0.707F); } rmNodeSetSceneCamera3D(camNode, c); rmCamera3DDelete(c); /* use RM's default lighting model */ rmDefaultLighting(camNode); } #define DIVISOR 1./32767. void ToVertex(int r1, int r2, RMvertex3D *v, RMvertex3D *n) { double azimuth, elevation; double ca,sa,se,ce; /* input rand numbers in range 0..32767 */ /* convert to range 0..1 */ azimuth = (double)r1 * DIVISOR; elevation = (double)r2 * DIVISOR; /* convert azimuth to range 0..2pi */ azimuth *= RM_PI * 2.0; /* convert elevation to range -pi/2..pi/2 */ elevation = (elevation - 0.5) * RM_PI; /* covert */ ca = cos(azimuth); sa = sin(azimuth); se = sin(elevation); ce = cos(elevation); v->y = se; v->x = ca * ce; v->z = sa * ce; n->x = v->x; n->y = v->y; n->z = v->z; } RMnode * makePointNode(RMvertex3D *v, RMvertex3D *n, RMcolor4D *c, int i) { char buf[24]; RMnode *node; RMprimitive *p = rmPrimitiveNew(RM_POINTS); sprintf(buf,"point%d",i); node = rmNodeNew(buf, RM_RENDERPASS_3D, RM_RENDERPASS_ALL); rmPrimitiveSetVertex3D(p,1,v,RM_COPY_DATA,NULL); rmPrimitiveSetNormal3D(p,1,n, RM_COPY_DATA, NULL); rmPrimitiveSetColor4D(p, 1, c, RM_COPY_DATA, NULL); rmNodeAddPrimitive (node, p); rmNodeComputeBoundingBox(node); return node; } int myCompareFunc(const void *a, const void *b) { int ia, ib; ia = *(int *)a; ib = *(int *)b; if (staticVerts[ia].z > staticVerts[ib].z) return 1; else return -1; } int myRenderOrderCallback(const RMnode *n, const RMstate *s, int *orderIndices, int nChildren) { int i; const RMmatrix *m; memcpy(staticIndices, orderIndices, sizeof(int)*nChildren); for (i=0;i<nChildren;i++) rmNodeGetCenter(n->children[i],staticVerts+i); /* transform points through composite transformation matrix */ m = rmStateGetModelViewMatrix(s); for (i=0;i<nChildren;i++) rmPointMatrixTransform(staticVerts+i, m, staticVerts+i); /* sort in on descending z-coordinate for b2f rendering */ qsort(staticIndices, nChildren, sizeof(int), myCompareFunc); memcpy(orderIndices, staticIndices, sizeof(int)*nChildren); return (nChildren); } void my_build_objs(void) { RMnode *objnode; RMenum rpass = RM_RENDERPASS_TRANSPARENT; /* RMenum rpass = RM_RENDERPASS_OPAQUE; */ /* make MyRoot and all children nodes transparent */ MyRoot = rmNodeNew(MyRootName,RM_RENDERPASS_3D, rpass); { RMnode *n = rmNodeNew("fbClear", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); RMcolor4D bgcolor={0.2,0.2,0.3,1.0}; /* * assign a background color to take effect at "MyRoot" */ rmNodeSetSceneBackgroundColor(n,&bgcolor); /* rmNodeAddChild(MyRoot, n); */ rmNodeAddChild(rmRootNode(), n); } rmNodeAddChild(rmRootNode(),MyRoot); { int npts = num_points; RMvertex3D *v,*n; RMcolor4D *c; int i; objnode = rmNodeNew("pointcloudRoot",RM_RENDERPASS_3D, rpass); v = rmVertex3DNew(npts); n = rmVertex3DNew(npts); c = (RMcolor4D *)malloc(sizeof(RMcolor4D)*npts); srand(time(NULL)); /* generate random points on a the surface of a sphere */ for (i=0;i<npts;i++) { int r1,r2; r1 = rand(); r2 = rand(); ToVertex(r1,r2,v+i,n+i); c[i].r = c[i].g = c[i].b = 0.5; c[i].r = v[i].y * 0.5 + 0.5; c[i].a = 0.5F; } for (i=0;i<npts;i++) rmNodeAddChild(objnode, makePointNode(v+i, n+i, c+i, i)); rmVertex3DDelete(v); rmVertex3DDelete(n); rmNodeComputeBoundingBox(objnode); rmNodeComputeCenterFromBoundingBox(objnode); rmNodeAddChild(MyRoot, objnode); if (doOrder == 1) rmNodeSetRenderOrderCallback(objnode, myRenderOrderCallback); /* malloc space for sort buffers to avoid realloc on each frame */ staticIndices = (int *)malloc(sizeof(int)*npts); staticVerts = rmVertex3DNew(npts); free((void *)c); } { RMvertex3D v[2]; v[0].x = v[0].y = v[0].z = -1.0; v[1].x = v[1].y = v[1].z = 1.0; rmNodeSetBoundingBox(MyRoot,v,v+1); } if (do_pointsize) { rmNodeSetPointSize(MyRoot,pointsize); printf(" setting pointsize to %g \n", pointsize); } } 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; rmNodeGetRotateMatrix(MyRoot,&old); rmMatrixMultiply(&old,&m,&old); rmNodeSetRotateMatrix(MyRoot,&old); rmFrame(p, rmRootNode()); } void myinitfunc(RMpipe *p, RMnode *n) { my_build_objs(); my_set_scene(rmRootNode(), rmPipeGetChannelFormat(p)); rmauxSetGeomTransform(MyRoot,p); rmauxSetCamera3DTransform(rmRootNode(), p); /* * set handler to reset aspect ratio when the window is resized. */ rmauxSetResizeFunc(p, rmRootNode(), rmauxDefaultResizeFunc); if (doStats != 0) rmStatsComputeDemography(rmRootNode()); if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE) rmFrame(p, rmRootNode()); rmFrame(p, rmRootNode()); } void myRenderFunc(RMpipe *p, RMnode *n) { if (doStats != 0) rmStatsStartTime(); rmFrame(p, n); if (doStats != 0) { rmStatsEndTime(); rmStatsPrint(); } } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND hWnd; void *fptr; RMenum channelFormat; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMpipe *lone_pipe=NULL; RMenum targetPlatform = RM_PIPE_WGL; int status; parse_args(__argc,__argv); #else int main(int ac, char *av[]) { RMpipe *lone_pipe=NULL; int status; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum channelFormat; 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. */ lone_pipe = rmPipeNew(targetPlatform); rmPipeSetProcessingMode(lone_pipe, 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(lone_pipe, 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(lone_pipe,hWnd, img_width, img_height); } #endif #ifdef RM_X { Window w; w = rmauxCreateXWindow(lone_pipe, (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(lone_pipe,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); /* uncomment this next line if you want the object to rotate while the user is idle. */ /* rmauxSetIdleFunc(lone_pipe, my_idle_func); */ /* * X-ism: once the window is created and assigned to the * rendering pipe, rmUsePipe 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(lone_pipe); rmauxSetRenderFunc(myRenderFunc); /* * set key handler function so this prog will exit on "q" key. */ rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc); rmauxEventLoop(lone_pipe, rmRootNode(), &msg); rmPipeDelete(lone_pipe); rmFinish(); #ifdef RM_WIN return( msg.wParam ); #else return(1); #endif }