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,¢er);
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(),¢er);
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