/* * 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: cones.c,v 1.15 2004/01/17 03:14:46 wes Exp $ * $Revision: 1.15 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: cones.c,v $ * Revision 1.15 2004/01/17 03:14:46 wes * Added command line args [-fr NN] and [-spin] to enable constant-rate * rendering and auto-spin mode, respectively. * * Revision 1.14 2003/07/25 21:56:19 wes * Minor tweaks to fix Win32 compile problem. * * Revision 1.13 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.12 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.11 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.10 2002/09/17 14:26:19 wes * Changed default visual to RM_MBUF_STEREO_CHANNEL. When this fails, * as detected by RM_WHACKED returned from rmPipeInit(), will fall back * to anaglyph stereo using RM_REDBLUE_STEREO_CHANNEL. * * Revision 1.9 2002/08/19 00:25:37 wes * No signficant changes. * * Revision 1.8 2002/06/20 13:11:50 wes * Update copyright date. * * Revision 1.7 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.6 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.5 2001/03/31 16:55:18 wes * Added procmode.h, which defines an RMpipe processing mode used in * most demonstration programs. The default processing mode is * RM_PIPE_MULTISTAGE_VIEW_PARALLEL. * * Revision 1.4 2000/12/02 17:24:32 wes * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for * a summary of changes. With this checkin, these demo programs * are no longer compatible with versions of the OpenRM API that * are pre-1.4.0. * * Revision 1.3 2000/08/28 01:38:18 wes * Updated rmaux* interfaces - rmauxEventLoop now takes two additional * parameters (keypress and window resize app callbacks); replaced * rmauxUI with rmauxSetGeomTransform and, where appropriate, * rmauxSetCamera3DTransform. * * Revision 1.2 2000/04/20 18:04:34 wes * Minor tweaks and reorg for OpenRM 1.2.1. * * Revision 1.1.1.1 2000/02/28 21:55:30 wes * OpenRM 1.2 Release * * Revision 1.11 2000/02/28 17:21:54 wes * RM 1.2, pre-OpenRM * */ /* * usage: cones [-fr NN] [-spin] * options: * -fr NN sets the frameRate in maximum frames per second. If left * unspecified, no frame rate is imposed, and the program is freely running. * -spin will enable auto-spin mode. */ #include <rm/rm.h> #include <rm/rmaux.h> #include "procmode.h" static RMnode *MyRoot; int img_width=400,img_height=300; RMenum static_stereo_format = RM_MONO_CHANNEL; static int frameRate = -1; static RMenum spinEnable = RM_FALSE; void usage(char *progName) { printf(" usage: %s [-fr NN] [-spin] \n \ options: \n \ -fr NN sets the frameRate in maximum frames per second. \n \ If left unspecified, no frame rate is imposed, and the program \n \ is freely running. \n \ -spin will enable auto-spin mode. Default is no auto-spin. \n", progName); } void my_set_scene(RMnode *camNode, int stereo_format) { /* * here, we compute the camera parameters such that all of * the geometry is visible. */ RMcamera3D *c=rmCamera3DNew(); /* * assign the camera some "default" parameters. */ rmDefaultCamera3D(c); /* adjust the camera "default" parameters as a function of the geom contained at or below MyRoot */ 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); } /* write the 3d camera back as a scene parameter of rmRootNode() */ rmNodeSetSceneCamera3D(camNode, c); rmCamera3DDelete(c); /* because a copy is made by RM */ { /* * assign a background color to take effect at "MyRoot" */ RMcolor4D bgcolor={0.4F, 0.4F, 0.4F, 1.0F}; RMcolor4D unlit={1.0F, 1.0F, 1.0F, 1.0F}; rmNodeSetSceneBackgroundColor(MyRoot, &bgcolor); rmNodeSetUnlitColor(MyRoot, &unlit); } /* use RM's default lighting model */ rmDefaultLighting(camNode); } void my_build_objs(void) { MyRoot = rmNodeNew("MyRoot", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); { /* make some cones and put 'em at x=+5,y=+5 and z=+5 */ RMvertex3D v[6] = {{5.,0.,0.,}, /* base */ {8.,0.,0.}, /* top */ {0.,5.,0.}, /* base */ {0.,8.,0.}, /* top */ {0.,0.,5.}, /* base */ {0.,0.,8.}}; /* top */ float r[3] = {1.,1.,1.}; /* radius of each cone */ RMcolor3D c[3] = {{1.,0.,0.,},{0.,1.,0.},{0.,0.,1.}}; /* cone colors */ RMnode *coneobj; RMprimitive *coneprim; int ncones = 3; coneobj = rmNodeNew("cones",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); coneprim = rmPrimitiveNew(RM_CONES); /* * add the vertices: each corresponds to the center point * in the base of the cone. */ rmPrimitiveSetVertex3D(coneprim,ncones*2,v,RM_COPY_DATA,NULL); /* * add colors, if desired. each rgb-triplet will be used * to color the entire cone */ rmPrimitiveSetColor3D(coneprim, ncones, c, RM_COPY_DATA, NULL); /* * one radius value per cone. */ rmPrimitiveSetRadii(coneprim,ncones,r,RM_COPY_DATA, NULL); rmNodeAddPrimitive(coneobj, coneprim); rmNodeComputeBoundingBox(coneobj); rmNodeAddChild(MyRoot,coneobj); } { /* make some cylinders */ RMvertex3D v[6] = {{0.,0.,0}, {4.,0.,0.,}, {0.,0.,0},{0.,4.,0.}, {0.,0.,0},{0.,0.,4.}}; float r[3] = {.3F,.3F,.3F}; /* radius of each cylinder */ RMcolor3D c[3] = {{1.,1.,1.},{1.,1.,1.},{1.,1.,1.}}; /* cylinder colors */ RMnode *cylinderobj; RMprimitive *cylinderprim; int ncylinders = 3; cylinderobj = rmNodeNew("cylinders",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); cylinderprim = rmPrimitiveNew(RM_CYLINDERS); /* * add vertex info, 2 per cylinder. each pair defines the * endpoints of a cylinder. */ rmPrimitiveSetVertex3D(cylinderprim,ncylinders*2,v,RM_COPY_DATA,NULL); /* * one color value per cylinder. */ rmPrimitiveSetColor3D(cylinderprim, ncylinders, c, RM_COPY_DATA, NULL); /* * two radius values per cylinder, one for each end. these * radius values correspond to the vertex pair that defines * the endpoints of the cylinder. */ rmPrimitiveSetRadii(cylinderprim, ncylinders, r, RM_COPY_DATA, NULL); rmNodeAddPrimitive(cylinderobj, cylinderprim); rmNodeComputeBoundingBox(cylinderobj); rmNodeAddChild(MyRoot,cylinderobj); } { /* stick a sphere at the base of the cylinders to make things look nice */ RMnode *sphereobj; RMprimitive *sprim; RMvertex3D v; float radius=0.3F; RMcolor3D c={1.,1.,1.}; v.x = v.y = v.z = 0.0F; sphereobj = rmNodeNew("sphere",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); sprim = rmPrimitiveNew(RM_SPHERES); rmPrimitiveSetVertex3D(sprim,1,&v,RM_COPY_DATA,NULL); rmPrimitiveSetColor3D(sprim,1,&c,RM_COPY_DATA,NULL); rmPrimitiveSetRadii(sprim,1,&radius, RM_COPY_DATA, NULL); rmNodeAddPrimitive(sphereobj, sprim); rmNodeAddChild(MyRoot,sphereobj); } rmNodeUnionAllBoxes(MyRoot); /* this does union of bounding boxes in the entire tree, starting from the leaf nodes and going upwards.*/ { RMvertex3D bmin,bmax,center; rmNodeGetBoundingBox(MyRoot,&bmin,&bmax); center.x = bmin.x + 0.5F *(bmax.x - bmin.x); center.y = bmin.y + 0.5F *(bmax.y - bmin.y); center.z = bmin.z + 0.5F *(bmax.z - bmin.z); rmNodeSetCenter(MyRoot,¢er); } { RMvertex3D bmin,bmax,center; rmNodeUnionAllBoxes(rmRootNode()); rmNodeGetBoundingBox(rmRootNode(),&bmin,&bmax); center.x = bmin.x + 0.5F *(bmax.x - bmin.x); center.y = bmin.y + 0.5F *(bmax.y - bmin.y); center.z = bmin.z + 0.5F *(bmax.z - bmin.z); rmNodeSetCenter(rmRootNode(),¢er); } /* * add "my root" to RM's root node. this is necessary since we're * doing an rmSubtreeFrame() to render everything inside of rmauxUI(). */ rmNodeAddChild(rmRootNode(),MyRoot); } 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_build_objs(); my_set_scene(rmRootNode(), static_stereo_format); /* * 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); rmauxSetSpinEnable(spinEnable); rmauxSetCamera3DTransform(rmRootNode(),p); /* * set handler to reset aspect ratio when the window is resized. */ rmauxSetResizeFunc(p, rmRootNode(), rmauxDefaultResizeFunc); if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE) rmFrame(p, rmRootNode()); rmFrame(p, rmRootNode()); } void parseArgs(int ac, char *av[]) { int i=1; ac--; while (ac > 0) { if (strcmp(av[i],"-fr") == 0) { i++; ac--; sscanf(av[i],"%d",&frameRate); } else if (strcmp(av[i],"-spin") == 0) { spinEnable = RM_TRUE; } else { usage(av[0]); exit(-1); } i++; ac--; } } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; RMenum targetPlatform = RM_PIPE_WGL; HWND hWnd; void *fptr; RMpipe *lone_pipe=NULL; int status; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ /* * 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. */ static_stereo_format = RM_REDBLUE_STEREO_CHANNEL; parseArgs(__argc, __argv); #else /* assume RM_X */ int main(int ac, char *av[]) { void *msg; /* needed for rmauxEventLoop win32/unix API consistency */ RMenum targetPlatform = RM_PIPE_GLX; RMpipe *lone_pipe=NULL; int status; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ /* * 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. */ static_stereo_format = RM_REDBLUE_STEREO_CHANNEL; parseArgs(ac, av); #endif /* * 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); rmPipeSetFrameRate(lone_pipe, frameRate); #ifdef RM_WIN rmPipeSetChannelFormat(lone_pipe, RM_REDBLUE_STEREO_CHANNEL); /* * July 2003: * The test we use in X-based systems for the presence/absence of * a stereo visual does not work under windows. We need to investigate * and repair the problem. For now, the following code that dynamically * selects a multibuffered or anaglyph stereo visual works only on X-based * platforms. */ #else rmPipeSetChannelFormat(lone_pipe, RM_MBUF_STEREO_CHANNEL); if (rmPipeCreateContext(lone_pipe) == RM_WHACKED) { rmWarning(" alloc of a mbuf visual failed, falling back to anaglyph \n"); rmPipeSetChannelFormat(lone_pipe, RM_REDBLUE_STEREO_CHANNEL); if (rmPipeCreateContext(lone_pipe) == RM_WHACKED) { rmError(" this is hopeless. bye!"); exit(-1); } } #endif #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 by itself */ /* rmauxSetIdleFunc(lone_pipe,my_idle_func); */ /* * 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 inside RM. */ rmPipeMakeCurrent(lone_pipe); /* * 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 }