/* * 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: jballs.c,v 1.14 2003/04/13 18:13:23 wes Exp $ * $Revision: 1.14 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: jballs.c,v $ * Revision 1.14 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.13 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.12 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.11 2002/06/17 00:38:48 wes * updated copyright lines. * * Revision 1.10 2001/10/15 00:22:52 wes * Removed dead code. * * Revision 1.9 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.8 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.7 2001/03/31 18:48:43 wes * Turned off per-frame printf's. * * Revision 1.6 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.5 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.4 2000/08/31 02:14:46 wes * Added rmauxSetGeomTransform and rmauxSetCamera3DTransform. * * 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/16 14:43:27 wes * Changed "1" to RM_TRUE for variable "managed" passed to rmauxCreateXWindow. * * Revision 1.1.1.1 2000/02/28 21:55:30 wes * OpenRM 1.2 Release * * Revision 1.6 2000/02/28 17:21:55 wes * RM 1.2, pre-OpenRM * */ #include <rm/rm.h> #include <rm/rmaux.h> #include "procmode.h" static RMpipe *lone_pipe=NULL; static RMnode *MyRoot, *movinggroup, *stationarygroup; int img_width=800,img_height=600; RMenum static_stereo_format; 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(); rmDefaultCamera3D(c); /* assign it some default values. */ /* adjust the camera so 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); { /* * rather than use the default lighting, we're going to * roll our own here. */ RMlight *top; RMcolor4D speccolor; top = rmLightNew(); speccolor.r = speccolor.g = speccolor.b = .7; speccolor.a = 1.0; rmLightSetColor(top,NULL, NULL, &speccolor); rmNodeSetSceneLight(camNode, RM_LIGHT0, top); rmLightDelete(top); /* use default light model stuff from OpenGL */ } } void my_sphere(RMnode *addto, float cx, float cy, float cz, float radius, RMcolor3D *color) { RMprimitive *sprim; RMvertex3D v; v.x = cx; v.y = cy; v.z = cz; sprim = rmPrimitiveNew(RM_SPHERES); rmPrimitiveSetVertex3D(sprim,1,&v,RM_COPY_DATA,NULL); rmPrimitiveSetRadii(sprim, 1, &radius, RM_COPY_DATA, NULL); if (color != NULL) rmPrimitiveSetColor3D(sprim, 1, color, RM_COPY_DATA, NULL); if (color == NULL) rmPrimitiveSetModelFlag(sprim,RM_SPHERES_512); else rmPrimitiveSetModelFlag(sprim,RM_SPHERES_128); rmNodeAddPrimitive(addto, sprim); } void my_build_objs(void) { RMcolor3D color; RMnode *floor; /* * */ /* the root for all our stuff */ MyRoot = rmNodeNew("MyRoot",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); /* the root for the stationary objects */ stationarygroup = rmNodeNew("stationary",RM_RENDERPASS_3D, RM_RENDERPASS_ALL); /* build floor */ { int floor_res = 20; /* a 20x20 grid */ RMprimitive *p; RMvertex3D v[4] = {{-3.0, -2.0, 3.0},{3.0, -2.0, 3.0}, {3.0, -2.0, -3.0},{-3.0,-2.0,-3.0}}; RMvertex3D n={0.0F, 1.0F, 0.0F}; RMvertex3D *vlist; RMvertex3D *nlist; int iu,iv,indx; float x,dx,z,dz,y; /* y stays constant */ RMcolor4D speccolor={.9F, .9F, .9F, 1.F}; vlist = rmVertex3DNew(floor_res * floor_res); nlist = rmVertex3DNew(floor_res * floor_res); /* the floor needs to have a lot of quads/triangles so that the shading is correct. build arrays of verts & normals. */ z = v[0].z; dz = (v[3].z - v[0].z) / (float)(floor_res-1); y = v[0].y; indx = 0; for (iv = 0; iv < floor_res; iv++, z+=dz) { x = v[0].x; dx = (v[1].x - v[0].x)/(float)(floor_res-1); for (iu=0; iu < floor_res; iu++, x+=dx) { vlist[indx].x = x; vlist[indx].y = y; vlist[indx].z = z; nlist[indx] = n; indx++; } } floor = rmNodeNew("floor", RM_RENDERPASS_3D, RM_RENDERPASS_ALL); p = rmPrimitiveNew(RM_QUADMESH); rmPrimitiveSetQmeshDims(p,floor_res, floor_res); rmPrimitiveSetVertex3D(p,floor_res*floor_res,vlist, RM_COPY_DATA,NULL); rmPrimitiveSetNormal3D(p,floor_res*floor_res,nlist, RM_COPY_DATA,NULL); rmNodeAddPrimitive(floor,p); rmNodeComputeBoundingBox(floor); rmNodeAddChild(stationarygroup, floor); rmVertex3DDelete(vlist); rmVertex3DDelete(nlist); } /* build primary orb */ { RMnode *sloth; sloth = rmNodeNew("sloth", RM_RENDERPASS_3D, RM_RENDERPASS_ALL); my_sphere(sloth, 0.0F, 0.0F, 0.0F, 1.0F, NULL); rmNodeComputeBoundingBox(sloth); rmNodeAddChild(stationarygroup, sloth); } { /* build zippy orb */ RMvertex3D c={0.0F, 0.0F, 0.0F}; RMvertex3D pos={1.5F, -1.0F, 0.0F}; RMcolor4D red={1.0,0.0,0.0,1.0}; RMnode *zippy; RMlight *redLight; movinggroup = rmNodeNew("moving group", RM_RENDERPASS_3D, RM_RENDERPASS_ALL); color.r = .9F; color.g = color.b = 0.25F; zippy = rmNodeNew("zippy", RM_RENDERPASS_3D, RM_RENDERPASS_ALL); my_sphere(zippy, pos.x, pos.y, pos.z, 0.25F, &color); { float specexp = 10.F; rmNodeSetSpecularExponent(zippy, specexp); } rmNodeComputeBoundingBox(zippy); rmNodeSetCenter(movinggroup,&c); rmNodeAddChild(movinggroup, zippy); redLight = rmLightNew(); rmLightSetXYZ(redLight, &pos); rmLightSetColor(redLight, NULL, &red, &red); rmLightSetType (redLight, RM_LIGHT_POINT); rmNodeSetSceneLight(movinggroup,RM_LIGHT3,redLight); rmNodeAddChild(MyRoot, movinggroup); } rmNodeAddChild(movinggroup, stationarygroup); rmNodeAddChild(rmRootNode(),MyRoot); /* this does union of bounding boxes in the entire tree, starting from the leaf nodes and going upwards.*/ rmNodeUnionAllBoxes(rmRootNode()); rmNodeComputeCenterFromBoundingBox(rmRootNode()); rmNodeComputeCenterFromBoundingBox(MyRoot); { RMcolor4D bgcolor={0.2,0.2,0.3,1.0}; /* * assign a background color to take effect at "MyRoot" */ rmNodeSetSceneBackgroundColor(MyRoot,&bgcolor); } rmStatsComputeDemography(rmRootNode()); } int my_idle_func(RMpipe *p, int pointerX, int pointerY) { 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(movinggroup,&old) == RM_WHACKED) rmMatrixIdentity(&old); rmMatrixMultiply(&old,&m,&old); rmNodeSetRotateMatrix(movinggroup,&old); rmMatrixInverse(&m,&m); if (rmNodeGetRotateMatrix(stationarygroup,&old) == RM_WHACKED) rmMatrixIdentity(&old); rmMatrixMultiply(&old,&m,&old); rmNodeSetRotateMatrix(stationarygroup,&old); rmFrame(lone_pipe, rmRootNode()); return 1; } void myinitfunc(RMpipe *p, RMnode *n) { int do_print = 1; /* hack */ my_build_objs(); my_set_scene(rmRootNode(), rmPipeGetChannelFormat(p)); rmauxSetGeomTransform(MyRoot,p); rmauxSetCamera3DTransform(rmRootNode(), p); do_print=0; /* 5/5/99 */ if (do_print == 1) /* won't see output in win32 unless you change NULL to a char string with a filename, then the output will be written in ASCII format to that file. */ rmPrintSceneGraph(rmRootNode(),RM_PRINT_VERBOSE,NULL); /* * set handler to reset aspect ratio when the window is resized. */ rmauxSetResizeFunc(p, rmRootNode(), rmauxDefaultResizeFunc); } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND hWnd; RMenum channelFormat; RMenum targetPlatform = RM_PIPE_WGL; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ void *fptr; #else /* assume RM_X */ int main() { void *msg; /* needed for rmauxEventLoop */ RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_GLX; RMenum channelFormat; #endif int status; /* * 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; int managed = RM_TRUE; w = rmauxCreateXWindow(lone_pipe, (Window)NULL, /* parent window */ 0,0,img_width,img_height, "RM for X-Windows","icon-title",managed); /* * 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); /* * 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); /* uncomment this next line if you want the object to rotate by itself while the user is idle. */ rmauxSetIdleFunc(lone_pipe,my_idle_func); /* * 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 }