1 /*
2  * Copyright (C) 1997-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: cones.c,v 1.16 2005/06/08 18:28:49 wes Exp $
26  * $Revision: 1.16 $
27  * $Name: OpenRM-1-6-0-2-c $
28  * $Log: cones.c,v $
29  * Revision 1.16  2005/06/08 18:28:49  wes
30  * Code cleanup to eliminate compiler warnings.
31  *
32  * Revision 1.15  2004/01/17 03:14:46  wes
33  * Added command line args [-fr NN] and [-spin] to enable constant-rate
34  * rendering and auto-spin mode, respectively.
35  *
36  * Revision 1.14  2003/07/25 21:56:19  wes
37  * Minor tweaks to fix Win32 compile problem.
38  *
39  * Revision 1.13  2003/04/13 18:13:23  wes
40  * Updated copyright dates.
41  *
42  * Revision 1.12  2003/01/27 05:07:07  wes
43  * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
44  *
45  * Revision 1.11  2003/01/16 22:22:45  wes
46  * Updated all source files to reflect new organization of header files: all
47  * headers that were formerly located in include/rmaux, include/rmv and
48  * include/rmi are now located in include/rm.
49  *
50  * Revision 1.10  2002/09/17 14:26:19  wes
51  * Changed default visual to RM_MBUF_STEREO_CHANNEL. When this fails,
52  * as detected by RM_WHACKED returned from rmPipeInit(), will fall back
53  * to anaglyph stereo using RM_REDBLUE_STEREO_CHANNEL.
54  *
55  * Revision 1.9  2002/08/19 00:25:37  wes
56  * No signficant changes.
57  *
58  * Revision 1.8  2002/06/20 13:11:50  wes
59  * Update copyright date.
60  *
61  * Revision 1.7  2001/07/15 22:33:19  wes
62  * Added rmPipeDelete to the end of all demo progs. For those that use
63  * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen).
64  *
65  * Revision 1.6  2001/06/03 19:44:05  wes
66  * Add calls to new rmaux routines to handle window resize events, and
67  * for keyboard event handling.
68  *
69  * Revision 1.5  2001/03/31 16:55:18  wes
70  * Added procmode.h, which defines an RMpipe processing mode used in
71  * most demonstration programs. The default processing mode is
72  * RM_PIPE_MULTISTAGE_VIEW_PARALLEL.
73  *
74  * Revision 1.4  2000/12/02 17:24:32  wes
75  * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for
76  * a summary of changes. With this checkin, these demo programs
77  * are no longer compatible with versions of the OpenRM API that
78  * are pre-1.4.0.
79  *
80  * Revision 1.3  2000/08/28 01:38:18  wes
81  * Updated rmaux* interfaces - rmauxEventLoop now takes two additional
82  * parameters (keypress and window resize app callbacks); replaced
83  * rmauxUI with rmauxSetGeomTransform and, where appropriate,
84  * rmauxSetCamera3DTransform.
85  *
86  * Revision 1.2  2000/04/20 18:04:34  wes
87  * Minor tweaks and reorg for OpenRM 1.2.1.
88  *
89  * Revision 1.1.1.1  2000/02/28 21:55:30  wes
90  * OpenRM 1.2 Release
91  *
92  * Revision 1.11  2000/02/28 17:21:54  wes
93  * RM 1.2, pre-OpenRM
94  *
95  */
96 
97 /*
98  * usage: cones [-fr NN] [-spin]
99  * options:
100  *  -fr NN sets the frameRate in maximum frames per second. If left
101  *    unspecified, no frame rate is imposed, and the program is freely running.
102  *  -spin  will enable auto-spin mode.
103  */
104 
105 #include <rm/rm.h>
106 #include <rm/rmaux.h>
107 #include "procmode.h"
108 
109 static RMnode *MyRoot;
110 int img_width=400,img_height=300;
111 
112 RMenum static_stereo_format = RM_MONO_CHANNEL;
113 static int frameRate = -1;
114 static RMenum spinEnable = RM_FALSE;
115 
116 void
usage(char * progName)117 usage(char *progName)
118 {
119     printf(" usage: %s [-fr NN] [-spin] \n \
120 options: \n \
121   -fr NN sets the frameRate in maximum frames per second. \n \
122       If left unspecified, no frame rate is imposed, and the program \n \
123       is freely running. \n \
124   -spin  will enable auto-spin mode. Default is no auto-spin. \n", progName);
125 }
126 
127 
128 void
my_set_scene(RMnode * camNode,int stereo_format)129 my_set_scene(RMnode *camNode,
130 	     int stereo_format)
131 {
132     /*
133      * here, we compute the camera parameters such that all of
134      * the geometry is visible.
135      */
136 
137     RMcamera3D *c=rmCamera3DNew();
138 
139     /*
140      * assign the camera some "default" parameters.
141      */
142     rmDefaultCamera3D(c);
143 
144     /* adjust the  camera "default" parameters as a function of the
145      geom contained at or below MyRoot */
146     rmCamera3DComputeViewFromGeometry(c,MyRoot,img_width,img_height);
147 
148     if (stereo_format != RM_MONO_CHANNEL)
149     {
150       rmCamera3DSetStereo(c,RM_TRUE);
151       rmCamera3DSetEyeSeparation(c,2.5F);
152       rmCamera3DSetFocalDistance (c,0.707F);
153 
154     }
155 
156     /* write the 3d camera back as a scene parameter of rmRootNode() */
157     rmNodeSetSceneCamera3D(camNode, c);
158 
159     rmCamera3DDelete(c);		/* because a copy is made by RM */
160 
161     {
162 	/*
163 	 * assign a background color to take effect at "MyRoot"
164 	 */
165 	RMcolor4D bgcolor={0.4F, 0.4F, 0.4F, 1.0F};
166 	RMcolor4D unlit={1.0F, 1.0F, 1.0F, 1.0F};
167 	rmNodeSetSceneBackgroundColor(MyRoot, &bgcolor);
168 	rmNodeSetUnlitColor(MyRoot, &unlit);
169     }
170 
171     /* use RM's default lighting model */
172     rmDefaultLighting(camNode);
173 }
174 
175 void
my_build_objs(void)176 my_build_objs(void)
177 {
178     MyRoot = rmNodeNew("MyRoot", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
179 
180     {
181 	/* make some cones and put  'em at x=+5,y=+5 and z=+5 */
182 	RMvertex3D v[6] = {{5.,0.,0.,}, /* base */
183 			     {8.,0.,0.}, /* top */
184 			     {0.,5.,0.}, /* base */
185 			     {0.,8.,0.}, /* top */
186 			     {0.,0.,5.}, /* base */
187 			     {0.,0.,8.}}; /* top */
188 
189 	float r[3] = {1.,1.,1.}; /* radius of each cone */
190 
191 	RMcolor3D c[3] = {{1.,0.,0.,},{0.,1.,0.},{0.,0.,1.}}; /* cone colors */
192 	RMnode *coneobj;
193 	RMprimitive *coneprim;
194 	int ncones = 3;
195 
196 	coneobj = rmNodeNew("cones",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
197 
198 	coneprim = rmPrimitiveNew(RM_CONES);
199 
200 	/*
201 	 * add the vertices: each corresponds to the center point
202 	 * in the base of the cone.
203 	 */
204 	rmPrimitiveSetVertex3D(coneprim,ncones*2,v,RM_COPY_DATA,NULL);
205 
206 	/*
207 	 * add colors, if desired. each rgb-triplet will be used
208 	 * to color the entire cone
209 	 */
210 
211 	rmPrimitiveSetColor3D(coneprim, ncones, c, RM_COPY_DATA, NULL);
212 
213 	/*
214 	 * one radius value per cone.
215 	 */
216 	rmPrimitiveSetRadii(coneprim,ncones,r,RM_COPY_DATA, NULL);
217 
218 	rmNodeAddPrimitive(coneobj, coneprim);
219 	rmNodeComputeBoundingBox(coneobj);
220 
221 	rmNodeAddChild(MyRoot,coneobj);
222 
223     }
224 
225     {
226 	/* make some cylinders */
227 
228 	RMvertex3D v[6] = {{0.,0.,0}, {4.,0.,0.,},
229 			 {0.,0.,0},{0.,4.,0.},
230 			 {0.,0.,0},{0.,0.,4.}};
231 
232 	float r[3] = {.3F,.3F,.3F}; /* radius of each cylinder */
233 	RMcolor3D c[3] = {{1.,1.,1.},{1.,1.,1.},{1.,1.,1.}}; /* cylinder colors */
234 
235 	RMnode *cylinderobj;
236 	RMprimitive *cylinderprim;
237 	int ncylinders = 3;
238 
239 	cylinderobj = rmNodeNew("cylinders",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
240 	cylinderprim = rmPrimitiveNew(RM_CYLINDERS);
241 
242 
243 	/*
244 	 * add vertex info, 2 per cylinder. each pair defines the
245 	 * endpoints of a cylinder.
246 	 */
247 	rmPrimitiveSetVertex3D(cylinderprim,ncylinders*2,v,RM_COPY_DATA,NULL);
248 
249 	/*
250 	 * one color value per cylinder.
251 	 */
252 
253 	rmPrimitiveSetColor3D(cylinderprim, ncylinders, c, RM_COPY_DATA, NULL);
254 
255 
256 	/*
257 	 * two radius values per cylinder, one for each end.  these
258 	 * radius values correspond to the vertex pair that defines
259 	 * the endpoints of the cylinder.
260 	 */
261 
262 	rmPrimitiveSetRadii(cylinderprim,
263 			    ncylinders,
264 			    r,
265 			    RM_COPY_DATA,
266 			    NULL);
267 
268 	rmNodeAddPrimitive(cylinderobj, cylinderprim);
269 	rmNodeComputeBoundingBox(cylinderobj);
270 
271 	rmNodeAddChild(MyRoot,cylinderobj);
272 
273     }
274 
275     {
276 	/* stick a sphere at the base of the cylinders to make
277 	 things look nice */
278 
279 	RMnode *sphereobj;
280 	RMprimitive *sprim;
281 	RMvertex3D v;
282 	float radius=0.3F;
283 	RMcolor3D c={1.,1.,1.};
284 
285 	v.x = v.y = v.z = 0.0F;
286 
287 	sphereobj = rmNodeNew("sphere",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
288 	sprim = rmPrimitiveNew(RM_SPHERES);
289 
290 	rmPrimitiveSetVertex3D(sprim,1,&v,RM_COPY_DATA,NULL);
291 	rmPrimitiveSetColor3D(sprim,1,&c,RM_COPY_DATA,NULL);
292 
293 	rmPrimitiveSetRadii(sprim,1,&radius, RM_COPY_DATA, NULL);
294 
295 	rmNodeAddPrimitive(sphereobj, sprim);
296 
297 	rmNodeAddChild(MyRoot,sphereobj);
298     }
299 
300     rmNodeUnionAllBoxes(MyRoot); /* this does union of bounding boxes in the
301 				 entire tree, starting from the leaf nodes
302 				 and going upwards.*/
303     {
304         RMvertex3D bmin,bmax,center;
305 
306 	rmNodeGetBoundingBox(MyRoot,&bmin,&bmax);
307 	center.x = bmin.x + 0.5F *(bmax.x - bmin.x);
308 	center.y = bmin.y + 0.5F *(bmax.y - bmin.y);
309 	center.z = bmin.z + 0.5F *(bmax.z - bmin.z);
310 
311 	rmNodeSetCenter(MyRoot,&center);
312     }
313 
314     {
315         RMvertex3D bmin,bmax,center;
316 
317         rmNodeUnionAllBoxes(rmRootNode());
318 
319 	rmNodeGetBoundingBox(rmRootNode(),&bmin,&bmax);
320 	center.x = bmin.x + 0.5F *(bmax.x - bmin.x);
321 	center.y = bmin.y + 0.5F *(bmax.y - bmin.y);
322 	center.z = bmin.z + 0.5F *(bmax.z - bmin.z);
323 
324 	rmNodeSetCenter(rmRootNode(),&center);
325     }
326 
327     /*
328      * add "my root" to RM's root node. this is necessary since we're
329      * doing an rmSubtreeFrame() to render everything inside of rmauxUI().
330      */
331     rmNodeAddChild(rmRootNode(),MyRoot);
332 
333 }
334 
335 void
my_idle_func(RMpipe * p,int ix,int iy)336 my_idle_func(RMpipe *p,
337 	     int ix,
338 	     int iy)
339 {
340     RMmatrix m,old;
341     double d,c,s;
342     rmMatrixIdentity(&m);
343 
344     d = RM_DEGREES_TO_RADIANS(1.0);
345     c = cos(d);
346     s = sin(d);
347     m.m[0][0] = m.m[2][2] = c;
348     m.m[0][2] = -s;
349     m.m[2][0] = s;
350 
351     if (rmNodeGetRotateMatrix(MyRoot,&old) == RM_WHACKED)
352 	rmMatrixIdentity(&old);
353 
354     rmMatrixMultiply(&old,&m,&old);
355     rmNodeSetRotateMatrix(MyRoot,&old);
356 
357     rmFrame(p, rmRootNode());
358 
359     /* foil compiler warnings */
360     ix = iy = 0;
361 }
362 
363 void
myinitfunc(RMpipe * p,RMnode * n)364 myinitfunc(RMpipe *p,
365 	   RMnode *n)
366 {
367 
368     my_build_objs();
369     my_set_scene(n, static_stereo_format);
370 
371     /*
372      * set up the event handler to apply geometric transformations at
373      * MyRoot. note that the lights and cameras are placed at rmRootNode().
374      * therefore, the rotations & scaling applied at MyRoot do not affect
375      * the cameras & lights since they are at a higher level in the
376      * scene graph than MyRoot.
377      */
378     rmauxSetGeomTransform(MyRoot,p);
379     rmauxSetSpinEnable(spinEnable);
380     rmauxSetCamera3DTransform(n, p);
381 
382     /*
383      * set handler to reset aspect ratio when the window is resized.
384      */
385     rmauxSetResizeFunc(p, n, rmauxDefaultResizeFunc);
386 
387     if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
388 	rmFrame(p, n);
389 
390     rmFrame(p, n);
391 }
392 
393 
394 void
parseArgs(int ac,char * av[])395 parseArgs(int ac,
396 	  char *av[])
397 {
398     int i=1;
399     ac--;
400 
401     while (ac > 0)
402     {
403 	if (strcmp(av[i],"-fr") == 0)
404 	{
405 	    i++;
406 	    ac--;
407 	    sscanf(av[i],"%d",&frameRate);
408 	}
409 	else if (strcmp(av[i],"-spin") == 0)
410 	{
411 	    spinEnable = RM_TRUE;
412 	}
413 	else
414 	{
415 	    usage(av[0]);
416 	    exit(-1);
417 	}
418 	i++;
419 	ac--;
420     }
421 }
422 
423 #ifdef RM_WIN
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)424 int WINAPI WinMain (HINSTANCE hInstance,
425 		    HINSTANCE hPrevInstance,
426                     LPSTR lpszCmdLine, int nCmdShow)
427 {
428     MSG      msg;
429     RMenum targetPlatform = RM_PIPE_WGL;
430     HWND     hWnd;
431     void *fptr;
432     RMpipe *lone_pipe=NULL;
433     RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
434 
435     /*
436      * pick a stereo format:
437      * RM_MONO_CHANNEL - plain old single-view
438      * RM_REDBLUE_STEREO_CHANNEL - left channel in red, right channel in cyan
439      * RM_BLUERED_STEREO_CHANNEL - left in cyan, right in red
440      * RM_MBUF_STEREO_CHANNEL - multibuffered stereo, requires special
441      *    hardware.
442      */
443     static_stereo_format = RM_REDBLUE_STEREO_CHANNEL;
444 
445     parseArgs(__argc, __argv);
446 
447 #else  /* assume RM_X */
448 
449 int
450 main(int ac,
451      char *av[])
452 {
453     void *msg;			/* needed for rmauxEventLoop
454 				 win32/unix API consistency */
455     RMenum targetPlatform = RM_PIPE_GLX;
456     RMpipe *lone_pipe=NULL;
457     RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
458 
459     /*
460      * pick a stereo format:
461      * RM_MONO_CHANNEL - plain old single-view
462      * RM_REDBLUE_STEREO_CHANNEL - left channel in red, right channel in cyan
463      * RM_BLUERED_STEREO_CHANNEL - left in cyan, right in red
464      * RM_MBUF_STEREO_CHANNEL - multibuffered stereo, requires special
465      *    hardware.
466      */
467     static_stereo_format = RM_REDBLUE_STEREO_CHANNEL;
468 
469     parseArgs(ac, av);
470 
471 #endif
472 
473     /*
474      * first stage of RM initialization.
475      */
476     rmInit();
477 
478     /*
479      * create the rendering pipe. this step is required in both
480      * Win32 and X.
481      */
482 
483     lone_pipe = rmPipeNew(targetPlatform);
484     rmPipeSetProcessingMode(lone_pipe, processingMode);
485     rmPipeSetFrameRate(lone_pipe, frameRate);
486 
487 #ifdef RM_WIN
488     rmPipeSetChannelFormat(lone_pipe, RM_REDBLUE_STEREO_CHANNEL);
489     /*
490      * July 2003:
491      * The test we use in X-based systems for the presence/absence of
492      * a stereo visual does not work under windows. We need to investigate
493      * and repair the problem. For now, the following code that dynamically
494      * selects a multibuffered or anaglyph stereo visual works only on X-based
495      * platforms.
496      */
497 #else
498     rmPipeSetChannelFormat(lone_pipe, RM_MBUF_STEREO_CHANNEL);
499 
500     if (rmPipeCreateContext(lone_pipe) == RM_WHACKED)
501     {
502 	rmWarning(" alloc of a mbuf visual failed, falling back to anaglyph \n");
503 	rmPipeSetChannelFormat(lone_pipe, RM_REDBLUE_STEREO_CHANNEL);
504 
505 	if (rmPipeCreateContext(lone_pipe) == RM_WHACKED)
506 	{
507 	    rmError(" this is hopeless. bye!");
508 	    exit(-1);
509 	}
510     }
511 #endif
512 
513 
514 #ifdef RM_WIN
515     {
516         /*
517 	 * Win32: when a window is created, we have to tell windows the
518 	 * name of the "WndProc," the procedure that gets called by
519 	 * windows with events (the event loop) (contrast to the X model
520 	 * where the name of the event loop is not part of the window).
521 	 * Since we're using RMaux, we know about the event handling
522 	 * procedure named "rmauxWndProc" and we provide that here.
523 	 */
524 
525         fptr = (void *)(rmauxWndProc);
526 	hWnd = rmauxCreateW32Window(lone_pipe,
527 			       NULL, /* no parent window */
528 			       20,20,img_width,img_height,"RM for Windows",
529 			       hInstance,fptr);
530 	if (hWnd == 0)
531 	  exit(-1);
532 
533 	/*
534 	 * assign the new window handle to the rendering pipe.
535 	 */
536 	rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height);
537     }
538 #endif
539 #ifdef RM_X
540     {
541 	Window w;
542 
543 	w = rmauxCreateXWindow(lone_pipe,
544 			       (Window)NULL, /* parent window */
545 			       0,0,img_width,img_height,
546 			       "RM for X-Windows","icon-title",RM_TRUE);
547 
548 	/*
549 	 * assign the window to the rendering pipe.
550 	 */
551 	rmPipeSetWindow(lone_pipe,w,img_width,img_height);
552 
553     }
554 #endif
555 
556     /*
557      * specify the name of the "init" function. the "init" function is
558      * mandatory in the Win32 world, and optional in the X world.
559      *
560      * in Win32, we don't want to call RM services until OpenGL is
561      * ready. we can be assured of readiness by using an init function
562      * with RMaux.
563      *
564      * in X, at this point, the window is mapped and OpenGL is ready,
565      * and we could call our init function directly.
566      */
567 
568     rmauxSetInitFunc(myinitfunc);
569 
570     /* uncomment this next line if you want the object to rotate
571        by itself */
572     /* rmauxSetIdleFunc(lone_pipe,my_idle_func);  */
573 
574     /*
575      * X-ism: once the window is created and assigned to the
576      * rendering pipe, rmPipeMakeCurrent makes the OpenGL rendering context
577      * current for the pipe+window combination.
578      *
579      * this step is required for X. in these demo programs, it is not
580      * strictly required by Win32, as the newly created context is made
581      * current as part of the OpenGL initialization sequence inside RM.
582      */
583     rmPipeMakeCurrent(lone_pipe);
584 
585     /*
586      * set key handler function so this prog will exit on "q" key
587      */
588     rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc);
589 
590     rmauxEventLoop(lone_pipe,rmRootNode(), &msg);
591 
592     rmPipeDelete(lone_pipe);
593     rmFinish();
594 
595 #ifdef RM_WIN
596     return( msg.wParam );
597 #else
598     return(1);
599 #endif
600 }
601