1 /*
2  * Copyright (C) 2000-2005, R3vis Corporation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA,
17  * or visit http://www.gnu.org/copyleft/gpl.html.
18  *
19  * Contributor(s):
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  *
22  * The OpenRM project is located at http://openrm.sourceforge.net/.
23  */
24 /*
25  * $Id: clipper.c,v 1.12 2005/06/12 21:31:00 wes Exp $
26  * $Revision: 1.12 $
27  * $Name: OpenRM-1-6-0-2-c $
28  * $Log: clipper.c,v $
29  * Revision 1.12  2005/06/12 21:31:00  wes
30  * Minor code reorganization.
31  *
32  * Revision 1.11  2005/06/08 18:22:14  wes
33  * Code cleanup to eliminate compiler warnings.
34  *
35  * Revision 1.10  2003/04/13 18:13:23  wes
36  * Updated copyright dates.
37  *
38  * Revision 1.9  2003/01/27 05:07:07  wes
39  * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
40  *
41  * Revision 1.8  2003/01/16 22:22:45  wes
42  * Updated all source files to reflect new organization of header files: all
43  * headers that were formerly located in include/rmaux, include/rmv and
44  * include/rmi are now located in include/rm.
45  *
46  * Revision 1.7  2002/06/17 00:34:39  wes
47  * replaced rmSubtreeFrame with rmFrame for v1.4.2.
48  *
49  * Revision 1.6  2001/07/15 22:33:18  wes
50  * Added rmPipeDelete to the end of all demo progs. For those that use
51  * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen).
52  *
53  * Revision 1.5  2001/06/03 19:44:05  wes
54  * Add calls to new rmaux routines to handle window resize events, and
55  * for keyboard event handling.
56  *
57  * Revision 1.4  2001/05/26 14:24:49  wes
58  * Added wireframe cubes.
59  *
60  * Revision 1.3  2001/03/31 16:55:18  wes
61  * Added procmode.h, which defines an RMpipe processing mode used in
62  * most demonstration programs. The default processing mode is
63  * RM_PIPE_MULTISTAGE_VIEW_PARALLEL.
64  *
65  * Revision 1.2  2000/12/02 17:24:32  wes
66  * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for
67  * a summary of changes. With this checkin, these demo programs
68  * are no longer compatible with versions of the OpenRM API that
69  * are pre-1.4.0.
70  *
71  * Revision 1.1  2000/08/31 02:14:17  wes
72  * Initial entry.
73  *
74  */
75 
76 /*
77  * This program demonstates the use of a transformable clip plane, along
78  * with transformation nesting and polygon draw mode manipulation.
79  * A sphere is positioned at the center of the view, with a red directional
80  * light source pointing in from the left, and a blue directional
81  * light source pointing in from the right. In addition, you'll see
82  * a single quad, which represents the position and orientation of
83  * the clip plane.
84  *
85  * The clip plane may be transformed (rotated) by using Shift+Button3.
86  * Button2 will rotate the entire model, Shift+Button2 will perform
87  * isometric scaling, Button1 translates the entire model parallel to
88  * image plane, and Button3 dollys the camera along the Z axis in
89  * eye coordinates.
90  */
91 
92 #include <rm/rm.h>
93 #include <rm/rmaux.h>
94 #include <time.h>
95 #include "procmode.h"
96 
97 /*
98  * usage: clipper [-w img_width] [-h img_height]
99  */
100 
101 static char MyRootName[]={"MyRoot"};
102 static RMnode *MyRoot;
103 static int img_width=400,img_height=300;
104 
105 static RMvertex3D defaultClipPlanePoint={0.0F, 0.0F, 0.0F};
106 static RMvertex3D defaultClipPlaneNormal={0.0F, 0.0F, 1.0F};
107 
108 static RMclipPlane clipPlane;
109 static RMnode *quadNode;
110 static RMnode *clipNode;
111 static float saveX, saveY;
112 static RMmatrix startMatrix, quadNodeMatrixInverse;
113 static RMmatrix MyRootMatrix, MyRootMatrixInverse;
114 
115 void
usage(char * av[])116 usage(char *av[])
117 {
118     char buf[256];
119     sprintf(buf," usage: %s [-w img_width] [-h img_height] \n",av[0]);
120     rmError(buf);
121 }
122 
123 void
parse_args(int ac,char * av[])124 parse_args(int ac,
125 	   char *av[])
126 {
127     int i;
128 
129     i = 1;
130     while (i < ac)
131     {
132 	if (strcmp(av[i],"-w") == 0)
133 	{
134 	    i++;
135 	    sscanf(av[i],"%d",&img_width);
136 	}
137 	else if (strcmp(av[i],"-h") == 0)
138         {
139 	    i++;
140 	    sscanf(av[i],"%d",&img_height);
141 	}
142 	else
143 	{
144 	    usage(av);
145 	    exit(-1);
146 	}
147 	i++;
148     }
149 }
150 
151 void
myLighting(RMnode * addToNode)152 myLighting(RMnode *addToNode)
153 {
154     /* set up two lights, one red and one blue */
155     RMlight   *l0, *l1;
156     RMcolor4D red = {1.0, 0.1, 0.1, 1.0};
157     RMcolor4D blue= {0.1, 0.1, 1.0, 1.0};
158 
159     RMcolor4D  specular = {0.5, 0.5, 0.5, 1.0};
160 
161     RMvertex3D redDirection = {-3.0, 2.0, 1.0};
162     RMvertex3D blueDirection = {3.0, 2.0, 1.0};
163 
164     l0 = rmLightNew();
165     if (l0 == NULL)
166 	return;
167 
168     rmLightSetType(l0, RM_LIGHT_DIRECTIONAL);
169     rmLightSetColor(l0, NULL, &red, &specular);
170     rmLightSetXYZ (l0, &redDirection);
171 
172     l1 = rmLightNew();
173     if (l1 == NULL)
174 	return;
175 
176     rmLightSetType(l1, RM_LIGHT_DIRECTIONAL);
177     rmLightSetColor(l1, NULL, &blue, &specular);
178     rmLightSetXYZ(l1, &blueDirection);
179 
180     rmNodeSetSceneLight(addToNode, RM_LIGHT0, l0);
181     rmNodeSetSceneLight(addToNode, RM_LIGHT1, l1);
182 
183     /*
184      * when the lights are added as scene parameters, RM makes a copy
185      * of them, and we don't need our RMlight objects anymore, so we
186      * need to delete them.
187      */
188 
189     rmLightDelete(l0);
190     rmLightDelete(l1);
191 
192     /* set up the light model/lighting environment */
193     {
194 	RMcolor4D     defAmbient =  {0.2, 0.2, 0.2, 1.0};
195 	RMlightModel *lm = rmLightModelNew();
196 
197 	if (lm == NULL)
198 	    return;
199 
200 	rmLightModelSetAmbient(lm, &defAmbient);
201 	rmLightModelSetTwoSided (lm, RM_FALSE);
202 	rmLightModelSetLocalViewer(lm, RM_FALSE);
203 	rmNodeSetSceneLightModel(addToNode, lm);
204 
205 	rmLightModelDelete (lm);
206     }
207 }
208 
209 void
my_set_scene(RMnode * camNode,RMenum stereo_format)210 my_set_scene(RMnode *camNode,
211 	     RMenum stereo_format)
212 {
213     /*
214      * here, we compute the camera parameters such that all of
215      * the geometry is visible.
216      */
217 
218     RMcamera3D *c=rmCamera3DNew();
219 
220     /* assign a default view */
221     rmDefaultCamera3D(c);
222 
223     /* adjust the default view so that all geom is visible */
224     rmCamera3DComputeViewFromGeometry(c,MyRoot,img_width,img_height);
225 
226     if (stereo_format != RM_MONO_CHANNEL)
227     {
228 	rmCamera3DSetStereo(c,RM_TRUE);
229 	rmCamera3DSetEyeSeparation(c,2.5F);
230 	rmCamera3DSetFocalDistance (c,0.707F);
231 
232     }
233     rmNodeSetSceneCamera3D(camNode, c);
234     rmCamera3DDelete(c);
235 
236     /* use a custom lighting model */
237     myLighting(rmRootNode());
238 }
239 
240 void
my_sphere(RMnode * addto,float cx,float cy,float cz,float radius,RMcolor3D * color)241 my_sphere(RMnode *addto,
242 	  float cx,
243 	  float cy,
244 	  float cz,
245 	  float radius,
246 	  RMcolor3D *color)
247 {
248     RMprimitive *sprim;
249     RMvertex3D v;
250 
251     v.x = cx;
252     v.y = cy;
253     v.z = cz;
254 
255     sprim = rmPrimitiveNew(RM_SPHERES);
256     rmPrimitiveSetVertex3D(sprim,1,&v,RM_COPY_DATA,NULL);
257 
258     rmPrimitiveSetRadii(sprim, 1, &radius, RM_COPY_DATA, NULL);
259 
260     if (color != NULL)
261 	rmPrimitiveSetColor3D(sprim, 1, color, RM_COPY_DATA, NULL);
262 
263     rmPrimitiveSetModelFlag(sprim,RM_SPHERES_512);
264 
265     rmNodeAddPrimitive(addto, sprim);
266 }
267 
268 void
my_quad(RMnode * addToNode,RMvertex3D * point,RMvertex3D * normal,float radius)269 my_quad(RMnode *addToNode,
270 	RMvertex3D *point,
271 	RMvertex3D *normal,
272 	float radius)
273 {
274     RMprimitive *p;
275     RMvertex3D v[4], n[4];
276 
277     /* need to add code that will create a quad that actually
278      honors the plane equation specified by "normal" */
279     v[0].z = v[1].z = v[2].z = v[3].z = point->z;
280 
281     v[0].x = point->x - radius;
282     v[0].y = point->y - radius;
283 
284     v[1].x = point->x + radius;
285     v[1].y = point->y - radius;
286 
287     v[2].x = point->x + radius;
288     v[2].y = point->y + radius;
289 
290     v[3].x = point->x - radius;
291     v[3].y = point->y + radius;
292 
293     n[0] = n[1] = n[2] = n[3] = *normal;
294 
295     p = rmPrimitiveNew(RM_QUADS);
296 
297     rmPrimitiveSetVertex3D(p, 4, v, RM_COPY_DATA, NULL);
298     rmPrimitiveSetNormal3D(p, 4, n, RM_COPY_DATA, NULL);
299 
300     rmNodeAddPrimitive(addToNode, p);
301 }
302 
303 void
myClipPlane(RMnode * addToNode,RMvertex3D * point,RMvertex3D * normal)304 myClipPlane(RMnode *addToNode,
305 	    RMvertex3D *point,
306 	    RMvertex3D *normal)
307 {
308     rmClipPlaneSetPointNormal(&clipPlane, point, normal);
309     rmClipPlaneEnable(&clipPlane);
310 
311     rmNodeSetSceneClipPlane(addToNode,RM_SCENE_CLIP_PLANE0, &clipPlane);
312 }
313 
314 void
buildInsideStuff(RMnode * addTo,float x,float y,float z,float radius)315 buildInsideStuff(RMnode *addTo,
316 		 float x,
317 		 float y,
318 		 float z,
319 		 float radius)
320 {
321     RMprimitive *p;
322     RMvertex3D v[4];
323     RMcolor3D c[2];
324 
325     p = rmPrimitiveNew(RM_BOX3D);
326 
327     v[0].x = x;
328     v[0].y = y;
329     v[0].z = z;
330 
331     v[1].x = v[0].x + radius * 0.5;
332     v[1].y = v[0].y + radius * 0.5;
333     v[1].z = v[0].z + radius * 0.5;
334 
335     v[2].x = x;
336     v[2].y = y;
337     v[2].z = z;
338 
339     v[3].x = v[2].x - radius * 0.5;
340     v[3].y = v[2].y - radius * 0.5;
341     v[3].z = v[2].z - radius * 0.5;
342 
343     c[0].r = c[0].g = 1.0;
344     c[0].b = 0.0;
345 
346     c[1].r = c[1].b = 0.0;
347     c[1].g = 1.0;
348 
349     rmPrimitiveSetVertex3D(p,4, v, RM_COPY_DATA,NULL);
350     rmPrimitiveSetColor3D(p,2,c,RM_COPY_DATA, NULL);
351 
352     rmNodeAddPrimitive(addTo, p);
353 
354 }
355 
356 void
my_build_objs(void)357 my_build_objs(void)
358 {
359     RMnode *sphereNode, *insideNode, *insideWireNode;
360     RMnode *nonClipNode;
361     RMcolor3D sphereColor={1.0F, 1.0F, 1.0F};
362     float radius = 5.0;
363 
364     MyRoot = rmNodeNew(MyRootName,RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
365     rmNodeAddChild(rmRootNode(),MyRoot);
366 
367     clipNode = rmNodeNew("clipNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
368     nonClipNode = rmNodeNew("nonClipNode", RM_RENDERPASS_3D,
369 			    RM_RENDERPASS_OPAQUE);
370 
371     rmNodeAddChild(MyRoot, clipNode);
372     rmNodeAddChild(MyRoot, nonClipNode);
373 
374     /* build objs */
375     sphereNode = rmNodeNew("sphereNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
376     my_sphere(sphereNode, 0.0F, 0.0F, 0.0F, radius, &sphereColor);
377     rmNodeComputeBoundingBox(sphereNode);
378 
379     /* put some stuff inside the sphere */
380     insideNode = rmNodeNew("insideNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
381     buildInsideStuff(insideNode, 0.0F, 0.0F, 0.0F, radius);
382 
383     insideWireNode = rmNodeNew("insideWireNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
384     buildInsideStuff(insideWireNode, 0.0F, 0.0F, 0.0F, radius);
385 
386     /* clip plane representation */
387     quadNode = rmNodeNew("quadNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
388     my_quad(quadNode, &defaultClipPlanePoint, &defaultClipPlaneNormal, radius);
389     rmNodeComputeBoundingBox(quadNode);
390     rmNodeSetPolygonDrawMode(quadNode, RM_BACK, RM_LINE);
391 
392     /* build clip plane */
393     myClipPlane(clipNode, &defaultClipPlanePoint, &defaultClipPlaneNormal);
394 
395     /* add stuff to the subtree that will have stuff clipped */
396     rmNodeAddChild(clipNode, sphereNode);
397     rmNodeAddChild(clipNode, insideNode);
398 
399     /* add stuff to the subtree where no clipping occurs */
400     rmNodeAddChild(nonClipNode, quadNode);
401 
402     rmNodeSetPolygonDrawMode(insideWireNode, RM_FRONT_AND_BACK, RM_LINE);
403     rmNodeSetShader(insideWireNode, RM_SHADER_NOLIGHT);
404     rmNodeAddChild(nonClipNode, insideWireNode);
405 
406     {
407 	RMcolor4D bgColor={0.2, 0.2, 0.3, 1.0};
408 	rmNodeSetSceneBackgroundColor(MyRoot, &bgColor);
409 
410     }
411     rmNodeUnionAllBoxes(rmRootNode());
412 }
413 
414 void
my_idle_func(RMpipe * p,int ix,int iy)415 my_idle_func(RMpipe *p,
416 	     int ix,
417 	     int iy)
418 {
419     RMmatrix m,old;
420     double d,c,s;
421     rmMatrixIdentity(&m);
422     d = RM_DEGREES_TO_RADIANS(1.0);
423     c = cos(d);
424     s = sin(d);
425     m.m[0][0] = m.m[2][2] = c;
426     m.m[0][2] = -s;
427     m.m[2][0] = s;
428 
429     rmNodeGetRotateMatrix(MyRoot,&old);
430     rmMatrixMultiply(&old,&m,&old);
431     rmNodeSetRotateMatrix(MyRoot,&old);
432 
433     rmFrame(p, rmRootNode());
434 
435     /* foil compiler warnings */
436     ix = iy = 0;
437 }
438 
pixeltovp(int ipixel,int idim)439 static float pixeltovp(int ipixel, int idim)
440 {
441     float t;
442     t = (float)(ipixel - (idim>>1)) / (float)(idim >> 1);
443     return(t);
444 }
445 
446 int
MyStartClipXformFunc(RMpipe * p,int ix,int iy)447 MyStartClipXformFunc(RMpipe *p,
448 		     int ix,
449 		     int iy)
450 {
451     int width, height;
452 
453     rmPipeGetWindowSize(p,&width, &height);
454 
455     /* save the starting position */
456     saveX = pixeltovp(ix,width);
457     saveY = -1.0F * pixeltovp(iy,height);
458     if (rmNodeGetRotateMatrix(quadNode, &startMatrix) == RM_WHACKED)
459 	rmMatrixIdentity(&startMatrix);
460 
461     rmMatrixInverse(&startMatrix, &quadNodeMatrixInverse);
462 
463     if (rmNodeGetRotateMatrix(MyRoot, &MyRootMatrix) == RM_WHACKED)
464 	rmMatrixIdentity(&MyRootMatrix);
465 
466     rmMatrixInverse(&MyRootMatrix, &MyRootMatrixInverse);
467 
468     rmMatrixMultiply(&startMatrix, &MyRootMatrix, &startMatrix);
469     return 1;
470 }
471 
472 int
MyClipXformFunc(RMpipe * p,int ix,int iy)473 MyClipXformFunc(RMpipe *p,
474 		int ix,
475 		int iy)
476 {
477     int width, height;
478     float newX, newY;
479     RMmatrix m;
480     RMvertex3D point,normal;
481 
482     rmPipeGetWindowSize(p,&width, &height);
483 
484     newX = pixeltovp(ix,width);
485     newY = -1.0F * pixeltovp(iy,height);
486 
487     /* compute the quaternion for x1,y1->x2,y2 */
488     rmauxArcBall (&saveX, &saveY, &newX, &newY, &m);
489 
490     /* update the rotation matrix on the quad */
491     rmMatrixMultiply(&m, &MyRootMatrixInverse, &m);
492     rmMatrixMultiply(&startMatrix, &m, &m);
493     rmNodeSetRotateMatrix(quadNode, &m);
494 
495     /* tweak the clip plane */
496     rmClipPlaneGetPointNormal(&clipPlane, &point, &normal);
497     rmPointMatrixTransform(&defaultClipPlaneNormal, &m, &normal);
498     rmClipPlaneSetPointNormal(&clipPlane, &point, &normal);
499     rmNodeSetSceneClipPlane(clipNode, RM_SCENE_CLIP_PLANE0, &clipPlane);
500 
501     /* rerender */
502 
503     rmFrame(p, rmRootNode());
504     return(1);
505 }
506 
507 void
myinitfunc(RMpipe * p,RMnode * n)508 myinitfunc(RMpipe *p,
509 	   RMnode *n)
510 {
511     my_build_objs();
512     my_set_scene(n, rmPipeGetChannelFormat(p));
513 
514     rmauxSetGeomTransform(MyRoot,p);
515     rmauxSetCamera3DTransform(n, p);
516 
517     /* shift+button3 rotates the clip plane around */
518     rmauxSetButtonDownFunc(RM_BUTTON3, RM_SHIFT_MODIFIER, MyStartClipXformFunc);
519     rmauxSetButtonMotionFunc(RM_BUTTON3, RM_SHIFT_MODIFIER, MyClipXformFunc);
520 
521     /*
522      * set handler to reset aspect ratio when the window is resized.
523      */
524     rmauxSetResizeFunc(p, n, rmauxDefaultResizeFunc);
525 
526     if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
527 	rmFrame(p, n);
528 
529     rmFrame(p, n);
530 }
531 
532 #ifdef RM_WIN
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)533 int WINAPI WinMain (HINSTANCE hInstance,
534 		    HINSTANCE hPrevInstance,
535                     LPSTR lpszCmdLine, int nCmdShow)
536 {
537     MSG      msg;
538     HWND     hWnd;
539     void *fptr;
540     RMpipe *lone_pipe=NULL;
541     RMenum targetPlatform = RM_PIPE_WGL;
542     RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
543     RMenum channelFormat;
544 
545     parse_args(__argc,__argv);
546 #else
547 int
548 main(int ac,
549      char *av[])
550 {
551     RMpipe *lone_pipe=NULL;
552     RMenum targetPlatform = RM_PIPE_GLX;
553     RMenum channelFormat;
554     RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
555     void *msg;			/* needed for rmauxEventLoop
556 				 win32/unix API consistency */
557 
558     parse_args(ac,av);
559 #endif
560     /*
561      * pick a stereo format:
562      * RM_MONO_CHANNEL - plain old single-view
563      * RM_REDBLUE_STEREO_CHANNEL - left channel in red, right channel in cyan
564      * RM_BLUERED_STEREO_CHANNEL - left in cyan, right in red
565      * RM_MBUF_STEREO_CHANNEL - multibuffered stereo, requires special
566      *    hardware.
567      */
568     channelFormat = RM_MONO_CHANNEL;
569 
570     /*
571      * first stage of RM initialization.
572      */
573     rmInit();
574 
575     /*
576      * create the rendering pipe. this step is required in both
577      * Win32 and X.
578      */
579 
580     lone_pipe = rmPipeNew(targetPlatform);
581 
582     /*
583      * set the processing mode on the RMpipe. this must happen before
584      * "rmPipeMakeCurrent().
585      */
586     rmPipeSetProcessingMode(lone_pipe, processingMode);
587     rmPipeSetDisplayListEnable(lone_pipe, RM_FALSE);
588 
589 #ifdef RM_WIN
590     {
591         /*
592 	 * Win32: when a window is created, we have to tell windows the
593 	 * name of the "WndProc," the procedure that gets called by
594 	 * windows with events (the event loop) (contrast to the X model
595 	 * where the name of the event loop is not part of the window).
596 	 * Since we're using RMaux, we know about the event handling
597 	 * procedure named "rmauxWndProc" and we provide that here.
598 	 */
599 
600         fptr = (void *)(rmauxWndProc);
601 	hWnd = rmauxCreateW32Window(lone_pipe,
602 			       NULL, /* no parent window */
603 			       20,20,img_width,img_height,"RM for Windows",
604 			       hInstance,fptr);
605 	if (hWnd == 0)
606 	  exit(-1);
607 
608 	/*
609 	 * assign the new window handle to the rendering pipe.
610 	 */
611 	rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height);
612     }
613 #endif
614 
615 #ifdef RM_X
616     {
617 	Window w;
618 
619 	w = rmauxCreateXWindow(lone_pipe,
620 			       (Window)NULL, /* parent window */
621 			       0,0,img_width,img_height,
622 			       "RM for X-Windows","icon-title",RM_TRUE);
623 	/*
624 	 * assign the window to the rendering pipe.
625 	 */
626 	rmPipeSetWindow(lone_pipe,w,img_width,img_height);
627 
628     }
629 #endif
630 
631     /*
632      * specify the name of the "init" function. the "init" function is
633      * mandatory in the Win32 world, and optional in the X world.
634      *
635      * in Win32, we don't want to call RM services until OpenGL is
636      * ready. we can be assured of readiness by using an init function
637      * with RMaux.
638      *
639      * in X, at this point, the window is mapped and OpenGL is ready,
640      * and we could call our init function directly.
641      */
642 
643     rmauxSetInitFunc(myinitfunc);
644 
645 
646     /* uncomment this next line if you want the object to rotate
647        while the user is idle. */
648     /*    rmauxSetIdleFunc(lone_pipe, my_idle_func); */
649 
650     /*
651      * X-ism: once the window is created and assigned to the
652      * rendering pipe, rmUsePipe makes the OpenGL rendering context
653      * current for the pipe+window combination.
654      *
655      * this step is required for X. in these demo programs, it is not
656      * strictly required by Win32, as the newly created context is made
657      * current as part of the OpenGL initialization sequence.
658      */
659     rmPipeMakeCurrent(lone_pipe);
660 
661     /*
662      * set key handler function so this prog will exit on "q" key.
663      */
664     rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc);
665 
666     rmauxEventLoop(lone_pipe,rmRootNode(), &msg);
667 
668     rmPipeDelete(lone_pipe);
669     rmFinish();
670 
671 #ifdef RM_WIN
672     return( msg.wParam );
673 #else
674     return(1);
675 #endif
676 
677 }
678