/* * 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/. */ /* * $Id: clrball.c,v 1.7 2003/04/13 18:13:23 wes Exp $ * $Revision: 1.7 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: clrball.c,v $ * 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:34:55 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:25:50 wes * Initial entry. * */ #include <rm/rm.h> #include <rm/rmaux.h> #include "procmode.h" int imgWidth=400,imgHeight=300; static RMnode *MyRoot=NULL, *MyOpaqueRoot=NULL, *MyTransparentRoot=NULL; 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 parameters */ /* adjust the "default" camera based upon MyRoot's geometry */ rmCamera3DComputeViewFromGeometry(c,rmRootNode(),imgWidth,imgHeight); if (stereo_format != RM_MONO_CHANNEL) { rmCamera3DSetStereo(c,RM_TRUE); rmCamera3DSetEyeSeparation(c,2.5F); rmCamera3DSetFocalDistance (c,0.707F); } /* write the camera back into the rmRootNode() */ rmNodeSetSceneCamera3D(camNode,c); rmCamera3DDelete(c); /* * make the background black for fun */ { RMcolor4D c; c.r = c.g = c.b = c.a = 0.0; /* * assign a background color to take effect at "MyRoot" */ rmNodeSetSceneBackgroundColor(MyOpaqueRoot,&c); } /* use RM's default lighting model, apply at rmRootNode() */ rmDefaultLighting(camNode); } void my_sphere(RMnode *addto, float cx, float cy, float cz, float radius, RMcolor4D *color, RMenum modelFlag, RMenum renderPassFlag) { RMnode *sn; RMprimitive *sprim; RMvertex3D v; sn = rmNodeNew("asphere",RM_RENDERPASS_3D, renderPassFlag); 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) rmPrimitiveSetColor4D(sprim, 1, color, RM_COPY_DATA, NULL); rmPrimitiveSetModelFlag(sprim,modelFlag); rmNodeAddPrimitive(sn, sprim); rmNodeComputeBoundingBox(sn); rmNodeAddChild(addto, sn); rmNodeUnionAllBoxes(addto); } void my_build_objs(void) { /* * Scene Graph layout & node scheduling: * * rmRootNode() - lights, camera * MyRoot - root for geom transforms * MyOpaqueRoot - root for opaque objects * MyTransparentRoot - root for 3D transparent objs */ RMcolor4D color; MyRoot = rmNodeNew("MyRoot",RM_RENDERPASS_3D, RM_RENDERPASS_ALL); MyOpaqueRoot = rmNodeNew("MyOpaqueRoot", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); MyTransparentRoot = rmNodeNew("MyTransparentRoot",RM_RENDERPASS_3D, RM_RENDERPASS_TRANSPARENT); /* the center sphere should be red. */ color.r = 1.0; color.g = color.b = 0.0; color.a = 1.0; my_sphere(MyOpaqueRoot,0.0F, 0.0F, 0.0F, 1.0F,&color,RM_SPHERES_32, RM_RENDERPASS_OPAQUE); /* transparent, blue sphere at (0,0,0) */ #if 0 color.r = color.g = 0.0; color.b = 1.0; color.a = 0.3; my_sphere(MyTransparentRoot,0.0F, 0.0F, 0.0F, 4.0F,&color); #endif my_sphere(MyTransparentRoot,0.0F, 0.0F, 0.0F, 3.0F,NULL,RM_SPHERES_512, RM_RENDERPASS_TRANSPARENT); { RMcolor4D white={1.0F, 1.0F, 1.0F, .5F}; RMcolor4D blue={0.1F, 0.1F, 1.0F, .75F}; RMcolor4D blue2={0.1F, 0.1F, 1.0F, 1.0F}; float exp=20.0; rmNodeSetDiffuseColor(MyTransparentRoot, &blue); rmNodeSetAmbientColor(MyTransparentRoot, &white); rmNodeSetSpecularColor(MyTransparentRoot, &blue2); rmNodeSetSpecularExponent(MyTransparentRoot, exp); } rmNodeAddChild(MyRoot, MyOpaqueRoot); rmNodeAddChild(MyRoot, MyTransparentRoot); rmNodeAddChild(rmRootNode(),MyRoot); rmNodeUnionAllBoxes(rmRootNode()); rmNodeComputeCenterFromBoundingBox(rmRootNode()); } 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(), 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. */ rmauxSetResizeFunc(p, rmRootNode(), rmauxDefaultResizeFunc); rmStatsComputeDemography(rmRootNode()); if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE) rmFrame(p, rmRootNode()); rmFrame(p, rmRootNode()); } void myrenderfunc(RMpipe *p, RMnode *n) { rmStatsStartTime(); rmFrame(p, n); rmStatsEndTime(); rmStatsPrint(); } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND hWnd; RMenum targetPlatform = RM_PIPE_WGL; void *fptr; #else /* assume RM_X */ int main() { void *msg; /* needed for rmauxEventLoop win32/unix API consistency */ RMenum targetPlatform = RM_PIPE_GLX; #endif int status; RMpipe *myPipe=NULL; RMenum stereoFormat; 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. */ stereoFormat = 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,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; int managed = RM_TRUE; w = rmauxCreateXWindow(myPipe, (Window)NULL, /* parent window */ 0,0,imgWidth,imgHeight, "RM for X-Windows","icon-title",managed); /* * assign the window to the rendering pipe. */ rmPipeSetWindow(myPipe,w,imgWidth,imgHeight); } #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 while the user is idle. */ /* rmauxSetIdleFunc(myPipe,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(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 }