1 /*
2 * Copyright (C) 1997-2005, R3vis Corporation.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library 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 GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18 *
19 * Original Contributor:
20 * Wes Bethel, R3vis Corporation, Marin County, California
21 * Additional Contributor(s):
22 *
23 * The OpenRM project is located at http://openrm.sourceforge.net/.
24 */
25 /*
26 * $Id: rmnode.c,v 1.25 2005/09/12 04:03:51 wes Exp $
27 * Version: $Name: OpenRM-1-6-0-2-RC2 $
28 * $Revision: 1.25 $
29 * $Log: rmnode.c,v $
30 * Revision 1.25 2005/09/12 04:03:51 wes
31 * Minor documentation updates.
32 *
33 * Revision 1.24 2005/06/26 18:55:32 wes
34 * Updates to rmPrintSceneGraph: added printing of fog scene parameter,
35 * made sure all current RMprimitive types are correctly reported.
36 *
37 * Revision 1.23 2005/06/15 02:10:40 wes
38 * Added initial support for application-settable defaults. Apps
39 * will use rmSetEnum/rmGetEnum to set or get defaults that are RMenums.
40 * The first round of variables that can be set/get by apps are
41 * RMnode traversal masks assigned to new scene graph nodes by rmNodeNew.
42 *
43 * Revision 1.22 2005/06/06 02:04:14 wes
44 * Added code that appears to be a bug in how many OpenGL implementations
45 * save/restore state when doing a glPushAttrib & clip planes. All that should
46 * be needed is GL_ENABLE_BIT and GL_TRANSFORM_BIT. It seems that lighting/
47 * color variables are corrupted in some cases, and added GL_LIGHTING_BIT
48 * seems to fix the problem (first shown in clipper.c).
49 *
50 * Revision 1.21 2005/05/28 16:24:22 wes
51 * Add new routines: rmNodeSetPickEnable, rmNodeGetPickEnable
52 *
53 * Revision 1.20 2005/03/16 16:39:15 wes
54 * Added more detailed RMprimitive output (colors, normals) in
55 * rmPrintSceneGraph.
56 *
57 * Revision 1.19 2005/02/27 19:34:04 wes
58 * Added support for application supplied texture object IDs and display lists.
59 *
60 * Revision 1.18 2005/02/19 16:35:16 wes
61 * Distro sync and consolidation.
62 * Beef up output from rmPrintSceneGraph.
63 *
64 * Revision 1.17 2005/01/23 17:08:25 wes
65 * Copyright updated to 2005.
66 * Updates to support access and use of extensions; multitexturing on all
67 * platforms, use of 3d texture extension to realize implementation of
68 * volume rendering and related functions on Windows platforms.
69 *
70 * Revision 1.16 2004/09/28 00:48:57 wes
71 * Added render state cache as a parameter to routines that may modify
72 * lighting state to fix a lighting state tracking problem.
73 *
74 * Revision 1.15 2004/08/07 17:11:32 wes
75 * Updates to rmPrintSceneGraph to show information about the fbClear
76 * operations at a node. This will be helpful in tracking down errors
77 * caused by having multiple fbClears, or fbClears contained in nodes
78 * scheduled for multiple rendering passes.
79 *
80 * Revision 1.14 2004/03/30 14:13:31 wes
81 * Fixed declarations and man page docs for several routines.
82 *
83 * Revision 1.13 2004/03/10 01:47:41 wes
84 * Removed a bunch of dead code, and added new routine rmNodeRemoveAllPrims.
85 *
86 * Revision 1.12 2004/01/16 16:46:09 wes
87 * Updated copyright line for 2004.
88 *
89 * Revision 1.11 2003/11/05 15:28:17 wes
90 * Revision update.
91 *
92 * Revision 1.9 2003/07/06 16:37:13 wes
93 * Minor typo in code update fixed.
94 *
95 * Revision 1.8 2003/07/06 16:32:22 wes
96 * Bug fix: refined test for enabling GL_LIGHTING_BIT in attrib mask in
97 * private_rmNodeComputeAttribMask(). Thanks to A. Yarnos for pointing
98 * out the problem.
99 *
100 * Revision 1.7 2003/06/14 03:17:13 wes
101 * rmNodeLineStyle - code addition.
102 *
103 * Revision 1.6 2003/05/04 15:58:15 wes
104 * Fixed rmNodeComputeBoundingBox bug that resulted in bogus bounding
105 * boxes in some circumstances.
106 *
107 * Revision 1.5 2003/04/05 14:11:24 wes
108 * Several changes:
109 * 1. renamed rmMutexDestroy to rmMutexDelete for API consistency.
110 * 2. fixed logic errors in rmNodeRemoveChild and rmNodeAddPrimitive so that
111 * mutexes are properly unlocked when error conditions are encountered.
112 * 3. RMstate's allocated in rmSceneGraphWalk and descendents are freed (leak).
113 *
114 * Revision 1.4 2003/02/14 00:17:14 wes
115 * No significant changes.
116 *
117 * Revision 1.3 2003/02/02 17:50:57 wes
118 * Added bounding boxes to RMprimitives, as a supplement to node-level bboxes.
119 * The RMprimitive level bboxes are needed for the retained-mode CR work.
120 *
121 * Revision 1.2 2003/02/02 02:07:15 wes
122 * Updated copyright to 2003.
123 *
124 * Revision 1.1.1.1 2003/01/28 02:15:23 wes
125 * Manual rebuild of rm150 repository.
126 *
127 * Revision 1.22 2003/01/27 05:04:42 wes
128 * Changes to RMpipe API and initialization sequence to unify GLX, WGL and CR
129 * platforms w/o too much disruption to existing apps.
130 *
131 * Revision 1.21 2003/01/16 22:21:17 wes
132 * Updated all source files to reflect new organization of header files:
133 * all header files formerly located in include/rmaux, include/rmi, include/rmv
134 * are now located in include/rm.
135 *
136 * Revision 1.20 2003/01/12 23:50:06 wes
137 * Minor adjustments to texturing environment controls to fix problems with
138 * the texture environment not being set correctly.
139 *
140 * Revision 1.19 2003/01/09 16:39:35 wes
141 * Minor documentation update to rmNodeComputeBoundingBox().
142 *
143 * Revision 1.18 2002/12/31 00:55:22 wes
144 *
145 * Various enhancements to support Chromium - achitecture-specific sections
146 * of RMpipe were cleaned up, etc.
147 *
148 * Revision 1.17 2002/12/04 14:50:33 wes
149 * Cleanup SGI compiles.
150 *
151 * Revision 1.16 2002/11/14 15:34:51 wes
152 * Minor editing for beautification.
153 *
154 * Revision 1.15 2002/09/22 17:34:49 wes
155 * Modified rmSubtreeDelete to not bother looking at an RMnode's refcount
156 * while making a decision about recursing. This will fix a memory leak
157 * reported by a user application.
158 *
159 * Revision 1.14 2002/08/17 15:14:15 wes
160 * Added a new routine: rmNodeSetRenderOrderCallback, which is used to
161 * reorder the render order of a node's children during the render traversal.
162 * this callback is invoked ONLY in the view traversal.
163 *
164 * Revision 1.13 2002/06/29 16:22:00 wes
165 * Tagged rmNodeSetOpacity and rmNodeGetOpacity as being deprecated.
166 *
167 * Revision 1.12 2002/06/10 20:47:13 wes
168 * Fixed a bug whereby the "unlit color" was not properly surviving
169 * a state push/pop. The unlit color at a node affects the diffuse
170 * material property, as well as setting the current RGBA color. By
171 * changing the mask from GL_CURRENT_BIT, which captures the current
172 * color, to GL_CURRENT_BIT | GL_LIGHTING_BIT, both the current RGBA
173 * color as well as current material colors are captured. This bug
174 * was revealed by the "pickTest" demo.
175 *
176 * Revision 1.11 2002/04/30 19:32:47 wes
177 * Updated copyright dates.
178 *
179 * Revision 1.10 2001/06/03 20:48:17 wes
180 * Removed unused variables to get rid of compile warnings.
181 *
182 * Revision 1.9 2001/03/31 17:12:39 wes
183 * v1.4.0-alpha-2 checkin.
184 *
185 * Revision 1.8 2000/12/03 22:33:55 wes
186 * Mods for thread-safety.
187 *
188 * Revision 1.7 2000/08/30 02:03:39 wes
189 * Fixed bug in rmNodeSetPolygonMode - added RM_BACK to validity
190 * check of input face parameter.
191 *
192 * Revision 1.6 2000/08/27 17:37:11 wes
193 * Minor tweaks to improve process of bbox computation.
194 *
195 * Revision 1.5 2000/08/23 23:16:02 wes
196 * Updates to rmNodeComputeBoundingBox: switch over RMprimitive type,
197 * added code to compute bboxes for spheres (contributed by jdb),
198 * moved lengthy code from rmNodeComputeBounding box to a "generic
199 * bbox compute" routine.
200 *
201 * Revision 1.4 2000/05/17 14:23:38 wes
202 * Fixed compile warnings on private_rmStateInit().
203 *
204 * Revision 1.3 2000/05/14 23:39:58 wes
205 * Single parms to state initialization for rmSceneGraphWalk().
206 *
207 * Revision 1.2 2000/04/20 16:29:28 wes
208 * Significant documentation additions (wes), some code rearrangement.
209 * See $RM/RELEASENOTES for specific details concerning API changes.
210 *
211 * Revision 1.1.1.1 2000/02/28 21:29:40 wes
212 * OpenRM 1.2 Checkin
213 *
214 * Revision 1.1.1.1 2000/02/28 17:18:48 wes
215 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
216 *
217 */
218
219 /* documentation of public routines is incomplete in this file. */
220
221 #include <math.h>
222 #include <rm/rm.h>
223 #include "rmprivat.h"
224
225 static RMnode *root = NULL; /* NOT thread-safe! */
226
227 /* PRIVATE declarations */
228 void private_rmSceneGraphWalk (RMnode *r, const RMstate *last, void (*userfunc)(RMnode *node, const RMstate *state, void *clientData), void *clientData);
229 internals_RMtransformationStruct *private_rmNodeTransformsNew (void);
230 void private_rmPrintSceneGraph (const RMnode *r, int level, RMenum pmode, FILE *f);
231 RMenum *private_rmEnumNew (int n);
232 _surface_properties *private_rmSurfacePropsNew (void);
233 _rendermode_properties *private_rmRenderModePropsNew (void);
234
235
236 /*
237 * ----------------------------------------------------
238 * @Name rmRootNode
239 @pstart
240 RMnode * rmRootNode (void)
241 @pend
242
243 @astart
244 No arguments.
245 @aend
246
247 @dstart
248
249 Returns a handle to the "RM Root Node." The RM Root Node is intended
250 to be used as the root for all application-built scene graphs,
251 although this isn't strictly necessary.
252
253 Some RM routines (notable, rmFrame()) assume that their activities
254 will commence at the rmRootNode() and proceed downward through all
255 relative nodes.
256
257 Applications may modify parameters of the rmRootNode(), but must not
258 delete it. Parameter modification include setting scene parameters,
259 such as the background color, etc. as well as assigning RMprimitives.
260
261 @dend
262 * ----------------------------------------------------
263 */
264 RMnode *
rmRootNode(void)265 rmRootNode (void)
266 {
267 return(root);
268 }
269
270
271 /*
272 * ----------------------------------------------------
273 * @Name rmNodeNew
274 @pstart
275 RMnode * rmNodeNew(char *name,
276 RMenum dimsTraversalMask,
277 RMenum opacityTraversalMask)
278 @pend
279
280 @astart
281 char *name - a character string, used to set the internal RMnode "name"
282 attribute (input).
283 RMenum dimsTraversalMask - an RMenum value, may be either RM_RENDERPASS_3D,
284 RM_RENDERPASS_2D or RM_RENDERPASS_ALL (input).
285 RMenum opacityTraversalMask - an RMenum value, may be one of
286 RM_RENDERPASS_OPAQUE, RM_RENDERPASS_TRANSPARENT or RM_RENDERPASS_ALL (input).
287 @aend
288
289 @dstart
290 This routine creates a new RMnode, and returns a handle to the caller upon
291 success, or NULL upon failure.
292
293 During render-time traversal of the scene graph, render parameters can be
294 compared against certain attributes of the RMnode, thereby terminating
295 depth traversal. Three such traversal masks exist at this time (February
296 2000): 2D vs. 3D, opaque pass vs. transparent pass, and for stereo rendering,
297 left vs. right channel. The first two attributes, opaque vs. transparent and
298 2D vs. 3D are specified at the time the RMnode is created. These may be
299 subsequently overridden, if desired, using rmNodeSetTraversalMaskOpacity()
300 or rmNodeSetTraversalMaskDims().
301
302 Control over the filter masks used to compare against RMnode attributes
303 is manipulated by the multipass rendering framework. (Feb 2000, this needs
304 to be written).
305
306 When created, the RMnode is assigned the following default values:
307
308 1. The "name" attribute of the RMnode is set to contain a copy of the input
309 string "name" (rmNodeSetName()).
310
311 2. The RMnode's bounding box minimum and maximum coordinates are set to
312 RM_MAXFLOAT and RM_MINFLOAT, respectively.
313
314 3. The node's "traverse enable" attribute is set to RM_TRUE.
315 4. The node's "pick enable" attribute is set to RM_TRUE.
316
317 4. The node is scheduled for rendering during all passes of a stereo
318 rendering (rmNodeSetTraversalMaskChannel()).
319
320 5. The node's transformation attributes, if any, are applied to
321 the "model-view" matrix stack (as opposed to the texture stack)
322 (rmNodeSetTransformMode()).
323
324 @dend
325 * ----------------------------------------------------
326 */
327
328 RMnode *
rmNodeNew(char * name,RMenum rVdims,RMenum rOpaque)329 rmNodeNew(char *name,
330 RMenum rVdims,
331 RMenum rOpaque)
332 {
333 RMnode *n;
334 RMvertex3D bmin,bmax,center;
335 RMenum t;
336
337 if ((rVdims != RM_RENDERPASS_2D) && (rVdims != RM_RENDERPASS_3D) && (rVdims != RM_RENDERPASS_ALL))
338 {
339 rmError("rmNodeNew(): the input parameter dimsTraversalMask is not valid.");
340 return(NULL);
341 }
342
343 if ((rOpaque != RM_RENDERPASS_OPAQUE) && (rOpaque != RM_RENDERPASS_TRANSPARENT) && (rOpaque != RM_RENDERPASS_ALL))
344 {
345 rmError("rmNodeNew(): the input parameter opacityTraversalMask is not valid.");
346 return(NULL);
347 }
348
349 /* new with component manager */
350 n = private_rmNodeNew();
351
352 if (RM_ASSERT(n,"rmNodeNew() error: malloc fails. \n") == RM_WHACKED)
353 return(NULL);
354
355 n->parent = NULL;
356 n->nchildren = 0;
357 n->children = NULL;
358 n->nprims = 0;
359 n->prims = NULL;
360
361 n->sprops = NULL;
362 n->rprops = NULL;
363 n->scene_parms = NULL;
364 n->fbClear = NULL;
365
366 n->transforms = NULL;
367
368 n->viewPretraverseCallback = NULL;
369 n->viewPosttraverseCallback = NULL;
370 n->viewSwitchCallback = NULL;
371 n->viewRenderOrderCallback = NULL;
372
373 n->renderPretraverseCallback = NULL;
374 n->renderPosttraverseCallback = NULL;
375
376 n->clientData = NULL;
377 n->clientDataFreeFunc = NULL;
378
379 n->attribMask = 0;
380 n->refcount = 0;
381 n->nodeMutex = NULL;
382
383 /* obtain current value for general traversal mask */
384 rmGetEnum(RM_DEFAULT_NODE_TRAVERSAL_MASK, &t);
385 rmNodeSetTraverseEnable(n, t);
386
387 /* obtain current value for pick traversal mask */
388 rmGetEnum(RM_DEFAULT_NODE_PICK_TRAVERSAL_MASK, &t);
389 rmNodeSetPickEnable(n, t);
390
391 /* give it a bogus bounding box */
392 bmin.x = bmin.y = bmin.z = RM_MAXFLOAT;
393 bmax.x = bmax.y = bmax.z = RM_MINFLOAT;
394 center.x = center.y = center.z = 0.0F;
395
396 rmNodeSetBoundingBox(n,&bmin,&bmax);
397 rmNodeSetCenter(n, ¢er);
398
399 rmNodeSetTraversalMaskChannel(n,RM_ALL_CHANNELS);
400
401 rmNodeSetName(n,name);
402
403 rmNodeSetTraversalMaskOpacity(n,rOpaque);
404 rmNodeSetTraversalMaskDims(n,rVdims);
405
406 return(n);
407 }
408
409 /*
410 * ----------------------------------------------------
411 * @Name rmNodeDelete
412 @pstart
413 RMenum rmNodeDelete (RMnode *toDelete)
414 @pend
415
416 @astart
417 RMnode *toDelete - a handle to an RMnode (modified).
418 @aend
419
420 @dstart
421
422 This routine is the opposite of rmNodeNew(), and will delete
423 resources associated with an RMnode, including scene parameters,
424 rendering parameters, and all RMprimitives. Returns RM_CHILL upon
425 success, or RM_WHACKED upon failure.
426
427 This routine deletes only a single RMnode. Use rmSubTreeDelete() to
428 delete an entire, connected, rooted scene graph.
429
430 RMnodes maintain an internal reference count; it is an error to
431 delete an RMnode that has a non-zero reference count. The RMnode
432 reference count is modified by adding nodes into the scene graph
433 (rmNodeAddChild), or by removing them from the scene graph
434 (rmNodeRemoveChild, rmNodeRemoveAllChildren). Attempts to delete
435 RMnodes with a non-zero reference count will not succeed, but will be
436 rewarded with an RM_WHACKED return status.
437
438 @dend
439 * ----------------------------------------------------
440 */
441 RMenum
rmNodeDelete(RMnode * r)442 rmNodeDelete (RMnode *r)
443 {
444 if (RM_ASSERT(r, "rmNodeDelete() error: the input RMnode pointer is NULL.") == RM_WHACKED)
445 return(RM_WHACKED);
446
447 private_rmNodeDelete(r);
448
449 return(RM_CHILL);
450 }
451
452
453 /*
454 * ----------------------------------------------------
455 * @Name rmSubTreeDelete
456 @pstart
457 RMenum rmSubTreeDelete (RMnode *toDelete)
458 @pend
459
460 @astart
461 RMnode *toDelete - a handle to an RMnode (modified).
462 @aend
463
464 @dstart
465
466 Use this routine to delete a collection of nodes in a scene graph
467 rooted at "toDelete". RM_CHILL is returned upon success, or
468 RM_WHACKED upon failure. This routine performs a depth-first,
469 left-to-right traversal of the scene graph rooted at "toDelete", and
470 performs an rmNodeDelete operation during the traversal.
471
472 @dend
473 * ----------------------------------------------------
474 */
475 RMenum
rmSubTreeDelete(RMnode * r)476 rmSubTreeDelete (RMnode *r)
477 {
478 int i, n;
479 RMnode *c;
480
481 /* first, free the children of this node, then free the stuff at this node */
482 if (r == NULL)
483 return(RM_WHACKED);
484
485 n = rmNodeGetNumChildren(r);
486
487 for (i=0; i < n; i++)
488 {
489 c = rmNodeGetIthChild(r, 0);
490 rmNodeRemoveChild (r, c);
491
492 /* if (private_rmNodeGetRefcount(c) == 0) */
493 /* 9/20/02 removed the condition - causes a memory leak under
494 some circumstances. Note that rmNodeDelete checks the refct
495 of the node - doing so again here is an error */
496 rmSubTreeDelete(c);
497 }
498 rmNodeDelete(r);
499
500 return(RM_CHILL);
501 }
502
503 /*
504 * ----------------------------------------------------
505 * @Name rmNodeSetName
506 @pstart
507 RMenum rmNodeSetName (RMnode *toModify,
508 const char *name)
509 @pend
510
511 @astart
512 RMnode *toModify - a handle to an RMnode (modified).
513
514 const char *name - a character string (input).
515 @aend
516
517 @dstart
518
519 Use this routine to set the "name" attribute of an RMnode. The input
520 string is copied into a character string inside the RMnode. The
521 maximum string length allowable is defined by the constant
522 RM_MAX_STRING_LENGTH. The RMnode name attribute is used when
523 searching for a named node.
524
525 RM_CHILL is returned upon success, and RM_WHACKED is returned upon
526 failure.
527
528 @dend
529 * ----------------------------------------------------
530 */
531 RMenum
rmNodeSetName(RMnode * n,const char * name)532 rmNodeSetName (RMnode *n,
533 const char *name)
534 {
535 if (RM_ASSERT(n, "rmNodeSetName() error: the input RMnode is NULL.") == RM_WHACKED)
536 return(RM_WHACKED);
537
538 if (strlen(name) >= RM_MAX_STRING_LENGTH)
539 {
540 rmError("rmNodeSetName() error: the length of the input string exceeds RM_MAX_STRING_LENGTH");
541 return(RM_WHACKED);
542 }
543
544 memset(n->object_info.name, 0, RM_MAX_STRING_LENGTH);
545 strcpy(n->object_info.name, name);
546
547 return(RM_CHILL);
548 }
549
550
551 /*
552 * ----------------------------------------------------
553 * @Name rmNodeGetName
554 @pstart
555 char * rmNodeGetName (const RMnode *toQuery)
556 @pend
557
558 @astart
559 const RMnode *toQuery - a handle to an RMnode to query (input).
560 @aend
561
562 @dstart
563
564 Use this routine to obtain a COPY of the name string attribute of an
565 RMnode. A character string is returned to the caller upon success,
566 otherwise NULL is returned.
567
568 Since the return string is a COPY malloc'ed off the heap, the caller
569 should free() the return string when it is no longer needed.
570
571 @dend
572 * ----------------------------------------------------
573 */
574 char *
rmNodeGetName(const RMnode * n)575 rmNodeGetName (const RMnode *n)
576 {
577 char *c;
578
579 if (RM_ASSERT(n, "rmNodeGetName() error: the input RMnode pointer is NULL.") == RM_WHACKED)
580 return(NULL);
581
582 c = malloc(sizeof(char)*strlen(n->object_info.name)+1); /* no strdup() ?? */
583 strcpy(c, n->object_info.name);
584
585 return(c);
586 }
587
588
589 /*
590 * ----------------------------------------------------
591 * @Name rmNodeSetTraverseEnable
592 @pstart
593 RMenum rmNodeSetTraverseEnable (RMnode *toModify,
594 RMenum newval)
595 @pend
596
597 @astart
598 RMnode *toModify - a handle to an RMnode to modify (modified).
599
600 RMenum newval - an RMenum value, may be either RM_TRUE or RM_FALSE
601 (input).
602 @aend
603
604 @dstart
605
606 Use this routine to modify the value of the "general traverse enable"
607 attribute of an RMnode. This attribute affects all frame-based traversals:
608 view, render, and pick. A newVal of RM_TRUE specifies that a node will be
609 processed during such traversals. A newVal of RM_FALSE inhibits processing
610 of the node toModify along with all its children.
611
612 As of RM version 1.6.0, the point during node processing when RM checks
613 the value of the traversal flag varies between multistage rendering processing,
614 serial rendering processing, and picking varies. This inconsistency will be
615 remedied in a future RM version. For multistage rendering, the traverse enable
616 flag is checked prior to any other node processing. For picking and serial
617 rendering, the traverse enable flag is processed after a node's pretraversal
618 callback.
619
620 By default, when an RMnode is created with rmNodeNew, the value of
621 this attribute is set to RM_TRUE.
622
623 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
624
625 @dend
626 * ----------------------------------------------------
627 */
628 RMenum
rmNodeSetTraverseEnable(RMnode * t,RMenum status)629 rmNodeSetTraverseEnable (RMnode *t,
630 RMenum status)
631 {
632 if (RM_ASSERT(t, "rmNodeSetTraverseEnable() error: the input RMnode is NULL.") == RM_WHACKED)
633 return(RM_WHACKED);
634
635 if ((status != RM_TRUE) && (status != RM_FALSE))
636 {
637 rmError("rmNodeSetTraverseEnable() enumeration error: new value is neither RM_TRUE nor RM_FALSE. \n");
638 return(RM_WHACKED);
639 }
640
641 t->object_info.posted = status;
642
643 return(RM_CHILL);
644 }
645
646
647 /*
648 * ----------------------------------------------------
649 * @Name rmNodeGetTraverseEnable
650 @pstart
651 RMenum rmNodeGetTraverseEnable (const RMnode *toQuery)
652 @pend
653
654 @astart
655 const RMnode *toQuery - a handle to an RMnode to query (input).
656 @aend
657
658 @dstart
659
660 Returns to the caller the RMnode's "traverse enable" attribute. Upon
661 success, the return value will be either RM_TRUE or RM_FALSE. A
662 return value of RM_WHACKED indicates an error condition.
663
664 @dend
665 * ----------------------------------------------------
666 */
667 RMenum
rmNodeGetTraverseEnable(const RMnode * t)668 rmNodeGetTraverseEnable (const RMnode *t)
669 {
670 if (RM_ASSERT(t, "rmNodeGetTraverseEnable() error: the input RMnode is NULL. ") == RM_WHACKED)
671 return(RM_WHACKED);
672
673 return(t->object_info.posted);
674 }
675
676 /*
677 * ----------------------------------------------------
678 * @Name rmNodeGetPickEnable
679 @pstart
680 RMenum rmNodeGetPickEnable (const RMnode *toQuery)
681 @pend
682
683 @astart
684 const RMnode *toQuery - a handle to an RMnode to query (input).
685 @aend
686
687 @dstart
688
689 Returns to the caller the RMnode's "pick enable" attribute. Upon
690 success, the return value will be either RM_TRUE or RM_FALSE. A
691 return value of RM_WHACKED indicates an error condition.
692
693 @dend
694 * ----------------------------------------------------
695 */
696 RMenum
rmNodeGetPickEnable(const RMnode * toQuery)697 rmNodeGetPickEnable (const RMnode *toQuery)
698 {
699 if (RM_ASSERT(toQuery, "rmNodeGetPickEnable() error: the input RMnode is NULL. ") == RM_WHACKED)
700 return(RM_WHACKED);
701
702 return(toQuery->object_info.pickEnable);
703 }
704
705 /*
706 * ----------------------------------------------------
707 * @Name rmNodeSetPickEnable
708 @pstart
709 RMenum rmNodeSetPickEnable (RMnode *toModify,
710 RMenum newval)
711 @pend
712
713 @astart
714 RMnode *toModify - a handle to an RMnode to modify (modified).
715
716 RMenum newval - an RMenum value, may be either RM_TRUE or RM_FALSE
717 (input).
718 @aend
719
720 @dstart
721
722 Use this routine to modify the "pick enable" attribute of an
723 RMnode. Returns RM_CHILL upon success, or RM_WHACKED upon failure.
724
725 The "pick enable" attribute controls whether or not a node is
726 traversed during a pick operation. It is similar to the "traverse
727 enable" attribute, but is used only during picks. The pick
728 enable attribute can be used to accelerate picking operations in
729 complex scenes where only a small subset of the scene is pickable.
730
731 By default, when an RMnode is created with rmNodeNew, the value of
732 this attribute is set to RM_TRUE.
733
734 @dend
735 * ----------------------------------------------------
736 */
737 RMenum
rmNodeSetPickEnable(RMnode * toModify,RMenum newVal)738 rmNodeSetPickEnable (RMnode *toModify,
739 RMenum newVal)
740 {
741 if (RM_ASSERT(toModify, "rmNodeSetPickEnable() error: the input RMnode is NULL.") == RM_WHACKED)
742 return(RM_WHACKED);
743
744 if ((newVal != RM_TRUE) && (newVal != RM_FALSE))
745 {
746 rmError("rmNodeSetPickEnable() enumeration error: new value is neither RM_TRUE nor RM_FALSE. \n");
747 return(RM_WHACKED);
748 }
749
750 toModify->object_info.pickEnable = newVal;
751
752 return(RM_CHILL);
753 }
754
755 /*
756 * ----------------------------------------------------
757 * @Name rmNodeAddChild
758 @pstart
759 RMenum rmNodeAddChild (RMnode *parent,
760 RMnode *child)
761 @pend
762
763 @astart
764 RMnode *parent - a handle to an RMnode (modified).
765
766 RMnode *child - a handle to an RMnode (input).
767 @aend
768
769 @dstart
770
771 Use this routine to establish a parent-child relationship between two
772 RM scene graph nodes. This routine is the fundamental building block
773 used to construct the scene graph. Returns RM_CHILL upon success or
774 RM_WHACKED upon failure.
775
776 The node "child" is appended to the list of children at node
777 "parent". It is not possible to insert a child at a specific index
778 within the array of children. This can be done explicitly by calling
779 rmNodeAddChild in order, thereby producing the sequence of children
780 desired at an RMnode.
781
782 There is no limit to the number of children nodes at an RMnode.
783
784 Feb 2001 - This routine is thread-safe. Multiple application threads may
785 simultaneously call this routine; thread-safety is provided by mutex
786 locks in the component manager.
787
788 @dend
789 * ----------------------------------------------------
790 */
791 RMenum
rmNodeAddChild(RMnode * parent,RMnode * child)792 rmNodeAddChild (RMnode *parent,
793 RMnode *child)
794 {
795 extern RMcompMgrHdr *global_RMnodePool;
796
797 if ((RM_ASSERT(parent, "rmNodeAddChild() error: the RMnode pointer 'parent' is NULL.") == RM_WHACKED) ||
798 (RM_ASSERT(child, "rmNodeAddChild() error: the RMnode pointer 'child' is NULL.") == RM_WHACKED))
799 return(RM_WHACKED);
800
801 /*
802 * rather than realloc each time, we should alloc a chunk of RMnode *'s,
803 * then when we fill it up, realloc a larger chunk.
804 */
805 if (rmMutexLock(global_RMnodePool->guard) == RM_WHACKED)
806 {
807 rmError("rmNodeAddChild() error: problem locking guard mutex in component manager. ");
808 return(RM_WHACKED);
809 }
810
811 parent->children = (RMnode **)realloc(parent->children, sizeof(RMnode *)*(parent->nchildren+1));
812 parent->children[parent->nchildren] = child;
813 parent->nchildren += 1;
814
815 /* increment reference count of child */
816 private_rmNodeIncrementRefcount(child);
817
818 if (rmMutexUnlock(global_RMnodePool->guard) == RM_WHACKED)
819 {
820 rmError("rmNodeAddChild() error: problem unlocking guard mutex in component manager. ");
821 return(RM_WHACKED);
822 }
823
824 return(RM_CHILL);
825 }
826
827
828 /*
829 * ----------------------------------------------------
830 * @Name rmNodeRemoveChild
831 @pstart
832 RMenum rmNodeRemoveChild (RMnode *parent,
833 RMnode *child)
834 @pend
835
836 @astart
837 RMnode *parent - a handle to an RMnode (modified).
838
839 RMnode *child - a handle to an RMnode (modified).
840 @aend
841
842 @dstart
843
844 Will remove the RMnode "child" from the list of children nodes at
845 "parent". If "child" is not a child node of "parent", RM_WHACKED is
846 returned. Otherwise, the reference to "child" is removed from
847 "parent" and the count of children is decremented by one.
848
849 Upon successful location of "child" in "parent," the array of
850 children will be modified so that it is contiguous. Otherwise, the
851 metaphor of number of children and child indexing (rmNodeGetIthChild)
852 would break.
853
854 Consider this example. Upon entry to rmNodeRemoveChild, the array of
855 children at an RMnode is:
856
857 [A B C D]
858
859 and we wish to remove child "B". Upon exit from this routine, the
860 array of children will look like this:
861
862 [A C D]
863
864 Feb 2000 - at this time, RMnodes only know about their children, but
865 not parents. Therefore, the RMnode child is not modified in any way.
866 In the future, RMnodes might know about their parents, in which case
867 "child" would be modified so that it has one fewer parent references.
868
869 Feb 2001 - this routine is thread-safe: multiple applications threads
870 may safely call this routine simultaneously. Thread-safety is implemented
871 by mutexes in the component manager.
872
873 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
874
875 @dend
876 * ----------------------------------------------------
877 */
878 RMenum
rmNodeRemoveChild(RMnode * parent,RMnode * child)879 rmNodeRemoveChild (RMnode *parent,
880 RMnode *child)
881 {
882 int index, i;
883 extern RMcompMgrHdr *global_RMnodePool;
884
885 if ((RM_ASSERT(parent, "rmNodeRemoveChild() error: the RMnode pointer 'parent' is NULL. ") == RM_WHACKED) ||
886 (RM_ASSERT(child, "rmNodeRemoveChild() error: the RMnode pointer 'child' is NULL.") == RM_WHACKED))
887 return(RM_WHACKED);
888
889 if (rmMutexLock(global_RMnodePool->guard) == RM_WHACKED)
890 {
891 rmError("rmNodeRemoveChild() error: problem locking guard mutex in component manager. ");
892 return(RM_WHACKED);
893 }
894
895 /* determine the index of this child */
896 for (i = 0; i < rmNodeGetNumChildren(parent); i++)
897 {
898 if (rmNodeGetIthChild(parent, i) == child)
899 break;
900 }
901
902 if (i == rmNodeGetNumChildren(parent)) /* didn't find it */
903 {
904 if (rmMutexUnlock(global_RMnodePool->guard) == RM_WHACKED)
905 {
906 rmError("rmNodeRemoveChild() error: problem unlocking guard mutex in component manager. ");
907 }
908 return(RM_WHACKED);
909 }
910
911
912 /* scooch down the rest of the children over */
913 index = i;
914 for (i = index; i < (rmNodeGetNumChildren(parent) - 1); i++)
915 private_rmNodeGetIthChild(parent, i) = private_rmNodeGetIthChild(parent, (i + 1));
916
917
918 private_rmNodeGetIthChild(parent, i) = NULL;
919
920 parent->nchildren -= 1;
921
922 private_rmNodeDecrementRefcount(child);
923
924 if (rmMutexUnlock(global_RMnodePool->guard) == RM_WHACKED)
925 {
926 rmError("rmNodeRemoveChild() error: problem unlocking guard mutex in component manager. ");
927 return(RM_WHACKED);
928 }
929
930 return(RM_CHILL);
931 }
932
933
934 /*
935 * ----------------------------------------------------
936 * @Name rmNodeRemoveAllChildren
937 @pstart
938 RMenum rmNodeRemoveAllChildren (RMnode *toModify)
939 @pend
940
941 @astart
942 RMnode *toModify - a handle to an RMnode (modified).
943 @aend
944
945 @dstart
946
947 Use this routine to break the parent-child relationship at a given
948 RMnode. This routine will simply remove all nodes from the list of
949 children at an RMnode, and set the count of the number of children to
950 zero. The child nodes are not deleted; only the reference to them is
951 removed from the input node.
952
953 RM_CHILL is returned upon success, or RM_WHACKED upon failure.
954
955 @dend
956 * ----------------------------------------------------
957 */
958 RMenum
rmNodeRemoveAllChildren(RMnode * toModify)959 rmNodeRemoveAllChildren (RMnode *toModify)
960 {
961 int i, n;
962
963 if (RM_ASSERT(toModify, "rmNodeRemoveAllChildren() error: the input RMnode is NULL. ") == RM_WHACKED)
964 return(RM_WHACKED);
965
966 /* quickly, remove all the children of an RMnode */
967 n = private_rmNodeGetNumChildren(toModify);
968 for (i = 0; i < n; i++)
969 {
970 RMnode *t;
971 t = private_rmNodeGetIthChild(toModify, i);
972 private_rmNodeDecrementRefcount(t);
973 private_rmNodeGetIthChild(toModify, i) = NULL;
974 }
975 private_rmNodeGetNumChildren(toModify) = 0;
976
977 return(RM_CHILL);
978 }
979
980
981 /*
982 * ----------------------------------------------------
983 * @Name rmNodeGetNumChildren
984 @pstart
985 int rmNodeGetNumChildren (const RMnode *toQuery)
986 @pend
987
988 @astart
989 const RMnode *toQuery - a handle to an RMnode (input).
990 @aend
991
992 @dstart
993
994 Returns to the caller the number of child nodes registered with the
995 input node. Upon failure, a value of -1 is returned.
996
997 @dend
998 * ----------------------------------------------------
999 */
1000 int
rmNodeGetNumChildren(const RMnode * to_query)1001 rmNodeGetNumChildren (const RMnode *to_query)
1002 {
1003 if (RM_ASSERT(to_query, "rmNodeGetNumChildren() error: input RMnode pointer is NULL.") == RM_WHACKED)
1004 return(-1);
1005 else
1006 return(private_rmNodeGetNumChildren(to_query));
1007 }
1008
1009
1010 /*
1011 * ----------------------------------------------------
1012 * @Name rmNodeGetNumPrims
1013 @pstart
1014 int rmNodeGetNumPrims (const RMnode *toQuery)
1015 @pend
1016
1017 @astart
1018 const RMnode *toQuery - a handle to an RMnode (input).
1019 @aend
1020
1021 @dstart
1022
1023 Returns to the caller the number of RMprimitives owned by an RMnode.
1024 If the input RMnode is NULL, a value of -1 is returned.
1025
1026 Related routines: rmNodeGetPrimitive() retrieves the i'th primitive
1027 from an RMnode; rmNodeAddPrimitive() adds a new RMprimitive to an
1028 RMnode.
1029
1030 @dend
1031 * ----------------------------------------------------
1032 */
1033 int
rmNodeGetNumPrims(const RMnode * n)1034 rmNodeGetNumPrims (const RMnode *n)
1035 {
1036 if (RM_ASSERT(n, "rmNodeGetNumPrimitives() error: input node is NULL.\n") == RM_WHACKED)
1037 return(-1);
1038
1039 return(n->nprims);
1040 }
1041
1042 /*
1043 * ----------------------------------------------------
1044 * @Name rmNodeRemoveAllPrims
1045 @pstart
1046 RMenum rmNodeRemoveAllPrims (RMnode *toModify)
1047 @pend
1048
1049 @astart
1050 RMnode *toModify - a handle to an RMnode (modified).
1051 @aend
1052
1053 @dstart
1054
1055 Use this routine to remove all the RMprimitives associated with an
1056 RMnode object. Upon successful completion, RM_CHILL will be returned,
1057 and the RMnode will contain no primitives. Failure will occur, and
1058 RM_WHACKED will be return, if the input RMnode is NULL.
1059
1060 If you pass in an RMnode with no RMprimitives, nothing happens to the
1061 RMnode and RM_CHILL will be returned.
1062
1063 @dend
1064 * ----------------------------------------------------
1065 */
1066 RMenum
rmNodeRemoveAllPrims(RMnode * toModify)1067 rmNodeRemoveAllPrims (RMnode *toModify)
1068 {
1069 int i, n;
1070
1071 if (RM_ASSERT(toModify, "rmNodeRemoveAllPrimitives() error: the input RMnode is NULL. ") == RM_WHACKED)
1072 return RM_WHACKED;
1073
1074 /* quickly, remove all the children of an RMnode */
1075 n = rmNodeGetNumPrims(toModify);
1076 for (i = 0; i < n; i++)
1077 {
1078 rmPrimitiveDelete(toModify->prims[i]);
1079 toModify->prims[i] = NULL;
1080 }
1081
1082 toModify->nprims = 0;
1083
1084 return RM_CHILL;
1085 }
1086
1087 /*
1088 * ----------------------------------------------------
1089 * @Name rmNodeGetIthChild
1090 @pstart
1091 RMnode * rmNodeGetIthChild (const RMnode *toQuery,
1092 int indx)
1093 @pend
1094
1095 @astart
1096 const RMnode *toQuery - a handle to an RMnode (input).
1097
1098 int indx - an integer index value.
1099 @aend
1100
1101 @dstart
1102
1103 Use this routine to obtain the RMnode handle of a child node. Since
1104 the "indx" parameter is a C-style index, an input value of zero
1105 refers to the first child, a value of one refers to the second child,
1106 an so forth.
1107
1108 Use rmNodeGetNumChildren to determine how many children are
1109 registered at a given RMnode.
1110
1111 Upon failure, NULL is returned.
1112
1113 @dend
1114 * ----------------------------------------------------
1115 */
1116 RMnode *
rmNodeGetIthChild(const RMnode * to_query,int indx)1117 rmNodeGetIthChild (const RMnode *to_query,
1118 int indx)
1119 {
1120 if (RM_ASSERT(to_query, "rmNodeGetIthChild() error: input RMnode pointer is NULL.") == RM_WHACKED)
1121 return(NULL);
1122
1123 if ((indx < 0) || (indx >= private_rmNodeGetNumChildren(to_query)))
1124 {
1125 rmError("rmNodeGetIthChild() error: the input indx is greater than or equal to the number of children owned by the RMnode 'to_query'.");
1126 return(NULL);
1127 }
1128 else
1129 return(private_rmNodeGetIthChild(to_query, indx));
1130 }
1131
1132
1133 /*
1134 * ----------------------------------------------------
1135 * @Name rmFindNamedNode
1136 @pstart
1137 RMnode * rmFindNamedNode (const RMnode *start,
1138 const char *name)
1139 @pend
1140
1141 @astart
1142 const RMnode *start - an RMnode handle (input).
1143
1144 const char *name - a search string (input).
1145 @aend
1146
1147 @dstart
1148
1149 This routine searches for an RMnode in a scene graph rooted at
1150 "start" that contains the name attribute that matches the string in
1151 the input parameter "name". Upon success, a handle to matching RMnode
1152 is returned to the caller, otherwise, NULL is returned.
1153
1154 Aug 2000 - eventually, this routine will be replaced by using a
1155 customizable traverser.
1156
1157 @dend
1158 * ----------------------------------------------------
1159 */
1160 RMnode *
rmFindNamedNode(const RMnode * start,const char * name)1161 rmFindNamedNode (const RMnode *start,
1162 const char *name)
1163 {
1164 /*
1165 * look for a node named "name" starting at the location "start".
1166 * return it's handle if we find it, NULL otherwise.
1167 */
1168 char *c;
1169 int i;
1170 RMnode *t = NULL;
1171
1172 if ((RM_ASSERT(start, "rmFindNamedNode() error: the input start RMnode handle is NULL.") == RM_WHACKED) ||
1173 (RM_ASSERT(name, "rmFindNamedNode() error: the input search string is NULL") == RM_WHACKED))
1174 return(NULL);
1175
1176 c = private_rmNodeGetName((RMnode *)start);
1177
1178 if (strcmp(c, name) == 0)
1179 return((RMnode *)start);
1180 else
1181 {
1182 for (i = 0; i < rmNodeGetNumChildren(start); i++)
1183 {
1184 if ((t = rmFindNamedNode(rmNodeGetIthChild(start, i), name)) != NULL)
1185 break;
1186 }
1187 }
1188 return(t);
1189 }
1190
1191 /*
1192 * Aug 2000: this routine is intentionally undocumented - it is
1193 * not yet finished.
1194 */
1195 void
rmSceneGraphWalk(const RMpipe * p,RMnode * r,void (* userfunc)(RMnode * node,const RMstate * state,void * clientData),void * clientData)1196 rmSceneGraphWalk (const RMpipe *p,
1197 RMnode *r,
1198 void (*userfunc)(RMnode *node,
1199 const RMstate *state,
1200 void *clientData),
1201 void *clientData)
1202 {
1203 /*
1204 * walk the scene graph.
1205 * at each node, create the render state and pass that, along
1206 * with the node to the user function. the user function
1207 * may provide a pointer to something, and it is dragged along
1208 * as well.
1209 */
1210 RMstate *s;
1211 int pushedAttribsReturn;
1212
1213 if (
1214 /* (RM_ASSERT(p, "rmSceneGraphWalk() error: the input RMpipe is NULL.") == RM_WHACKED) || */
1215 (RM_ASSERT(r, "rmSceneGraphWalk() error: the input RMnode is NULL") == RM_WHACKED) ||
1216 (RM_ASSERT((void *)userfunc, "rmSceneGraphWalk() error: the input userfunc is NULL. ") == RM_WHACKED))
1217 return;
1218
1219 s = rmStateNew();
1220 private_rmStateInit(p, s, (RMenum)GL_RENDER, NULL, NULL, NULL, NULL);
1221 private_collectAndApplyMatrices (s, r, NULL, GL_RENDER,
1222 &pushedAttribsReturn, RM_FALSE);
1223 private_updateSceneParms(r, s, RM_FALSE, 0, NULL, NULL);
1224 private_rmSceneGraphWalk(r, s, userfunc, clientData);
1225
1226 rmStateDelete(s);
1227 }
1228
1229
1230 /*
1231 * ----------------------------------------------------
1232 * @Name rmPrintSceneGraph
1233 @pstart
1234 void rmPrintSceneGraph (const RMnode *root,
1235 RMenum printMode,
1236 const char *fileName)
1237 @pend
1238
1239 @astart
1240 const RMnode *root - a handle to an RMnode (input).
1241
1242 RMenum printMode - an RMenum value specifying a level of
1243 verbosity. At this time (Feb 2000), this parameter is effectively
1244 ignore. Use RM_CHILL for the time being.
1245
1246 const char *fileName - a character string, or NULL (input).
1247 @aend
1248
1249 @dstart
1250
1251 This routine will print the contents of a scene graph rooted at
1252 "root", performing a depth first, left-to-right traversal.
1253
1254 If the fileName parameter is NULL, print occurs to stderr. Otherwise,
1255 printing will be directed to the named file.
1256
1257 Feb 2000- this routine is not fully implemented, nor fully
1258 functional. It provides only rudimentary information.
1259
1260 @dend
1261 * ----------------------------------------------------
1262 */
1263 void
rmPrintSceneGraph(const RMnode * r,RMenum printmode,const char * fname)1264 rmPrintSceneGraph (const RMnode *r,
1265 RMenum printmode,
1266 const char *fname)
1267 {
1268 FILE *f;
1269
1270 /* prints to stderr the contents of the scene graph starting at the node "r" */
1271 if (fname == NULL)
1272 f = stderr;
1273 else
1274 {
1275 f = fopen(fname, "w");
1276 if (f == NULL)
1277 {
1278 char buf[1024];
1279
1280 sprintf(buf, "rmPrintSceneGraph() error: unable to open the file named <%s>. \n", fname);
1281 rmError(buf);
1282 return;
1283 }
1284 }
1285
1286 private_rmPrintSceneGraph(r,0,printmode,f);
1287
1288 if (f != stderr)
1289 fclose(f);
1290 }
1291
1292
1293 /*
1294 * ----------------------------------------------------
1295 * @Name rmNodeSetBoundingBox
1296 @pstart
1297 RMenum rmNodeSetBoundingBox (RMnode *toModify,
1298 const RMvertex3D *vMin,
1299 const RMvertex3D *vMax)
1300 @pend
1301
1302 @astart
1303 RMnode *toModify - a handle to an RMnode (modified).
1304
1305 const RMvertex3D *vMin, *vMax - handles to RMvertex3D objects
1306 (input).
1307 @aend
1308
1309 @dstart
1310
1311 Use this routine to update the bounding box attribute of an RMnode.
1312 The bounding box vertices are specified in world coordinates. The
1313 bounding box is specified with a minimum and maximum vertex, implying
1314 that the bounding box is aligned with the axes of the world
1315 coordinate system.
1316
1317 Each of "vMin" and "vMax" are optional - if you do not wish to update
1318 either the min or max bounding box coordinate, use a value of NULL.
1319
1320 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1321
1322 @dend
1323 * ----------------------------------------------------
1324 */
1325 RMenum
rmNodeSetBoundingBox(RMnode * n,const RMvertex3D * vmin,const RMvertex3D * vmax)1326 rmNodeSetBoundingBox (RMnode *n,
1327 const RMvertex3D *vmin,
1328 const RMvertex3D *vmax)
1329 {
1330 if (RM_ASSERT(n, "rmNodeSetBoundingBox() error: the input RMnode is NULL.") == RM_WHACKED)
1331 return(RM_WHACKED);
1332
1333 if (vmin != NULL)
1334 VCOPY(vmin, &(n->bbox.min));
1335
1336 if (vmax != NULL)
1337 VCOPY(vmax, &(n->bbox.max));
1338
1339 return(RM_CHILL);
1340 }
1341
1342
1343 /*
1344 * ----------------------------------------------------
1345 * @Name rmNodeGetBoundingBox
1346 @pstart
1347 RMenum rmNodeGetBoundingBox (const RMnode *toQuery,
1348 RMvertex3D *vMinReturn,
1349 RMvertex3D *vMaxReturn)
1350 @pend
1351
1352 @astart
1353 const RMnode *toQuery - a handle to an RMnode (input).
1354
1355 RMvertex3D *vMinReturn, *vMaxReturn - handles to RMvertex objects
1356 (modified).
1357 @aend
1358
1359 @dstart
1360
1361 Use this routine to obtain the bounding box minimum and/or minimum
1362 coordinate(s) from an RMnode. Upon success, RM_CHILL is returned, and
1363 the RMnode's bounding box coordinates are copied into caller-supplied
1364 memory (if non-NULL). Otherwise, RM_WHACKED is returned.
1365
1366 @dend
1367 * ----------------------------------------------------
1368 */
1369 RMenum
rmNodeGetBoundingBox(const RMnode * n,RMvertex3D * vMinReturn,RMvertex3D * vMaxReturn)1370 rmNodeGetBoundingBox (const RMnode *n,
1371 RMvertex3D *vMinReturn,
1372 RMvertex3D *vMaxReturn)
1373 {
1374 if (RM_ASSERT(n, "rmNodeGetBoundingBox() error: the input RMnode is NULL.") == RM_WHACKED)
1375 return(RM_WHACKED);
1376
1377 if (vMinReturn != NULL)
1378 VCOPY(&(n->bbox.min), vMinReturn);
1379
1380 if (vMaxReturn != NULL)
1381 VCOPY(&(n->bbox.max),vMaxReturn);
1382
1383 return(RM_CHILL);
1384 }
1385
1386
1387 /*
1388 * ----------------------------------------------------
1389 * @Name rmNodeComputeCenterFromBoundingBox
1390 @pstart
1391 RMenum rmNodeComputeCenterFromBoundingBox (RMnode *toModify)
1392 @pend
1393
1394 @astart
1395 RMnode *toModify - a handle to an RMnode (modified).
1396 @aend
1397
1398 @dstart
1399
1400 This convenience routine will set the RMnode's "center point"
1401 attribute (rmNodeSetCenter()) with the geometric average of the
1402 RMnode's bounding box minimum and maximum coordinate.
1403
1404 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1405
1406 @dend
1407 * ----------------------------------------------------
1408 */
1409 RMenum
rmNodeComputeCenterFromBoundingBox(RMnode * node)1410 rmNodeComputeCenterFromBoundingBox (RMnode *node)
1411 {
1412 RMvertex3D c,bmin,bmax;
1413
1414 if (RM_ASSERT(node, "rmNodeComputeCenterFromBoundingBox() error: the input RMnode is NULL") == RM_WHACKED)
1415 return(RM_WHACKED);
1416
1417 rmNodeGetBoundingBox(node,&bmin,&bmax);
1418 c.x = 0.5 * (bmax.x - bmin.x) + bmin.x;
1419 c.y = 0.5 * (bmax.y - bmin.y) + bmin.y;
1420 c.z = 0.5 * (bmax.z - bmin.z) + bmin.z;
1421
1422 rmNodeSetCenter(node,&c);
1423
1424 return(RM_CHILL);
1425 }
1426
1427
1428 /*
1429 * ----------------------------------------------------
1430 * @Name rmNodeComputeBoundingBox
1431 @pstart
1432 RMenum rmNodeComputeBoundingBox (RMnode *toModify)
1433 @pend
1434
1435 @astart
1436 RMnode *toModify - a handle to an RMnode (modified).
1437 @aend
1438
1439 @dstart
1440
1441 This convenience routine will determine the minimum and maximum
1442 extents of all geometric primitives owned by an RMnode by performing
1443 a "union" of all RMprimitive's bounding boxes. Then, this routine
1444 will also set the RMnode's bounding box minimum and maximum coordinate
1445 to those minimum and maximum extents.
1446
1447 In addition, this routine will set the RMnode's center point to be the
1448 middle of the computed bounding box. Note that setting a center point will
1449 have an impact on geometric transformations, such as a rotation, that
1450 might be set later at the RMnode toModify.
1451
1452 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1453
1454 @dend
1455 * ----------------------------------------------------
1456 */
1457 RMenum
rmNodeComputeBoundingBox(RMnode * node)1458 rmNodeComputeBoundingBox (RMnode *node)
1459 {
1460 int i, nprims;
1461 RMvertex3D accumMin, accumMax, tmin, tmax, center;
1462 RMprimitive *p;
1463 RMenum haveValidBoxData = RM_FALSE;
1464
1465 if (RM_ASSERT(node, "rmNodeComputeBoundingBox() error: the input RMnode is NULL") == RM_WHACKED)
1466 return(RM_WHACKED);
1467
1468 nprims = rmNodeGetNumPrims(node);
1469 accumMin.x = accumMin.y = accumMin.z = RM_MAXFLOAT;
1470 accumMax.x = accumMax.y = accumMax.z = RM_MINFLOAT;
1471
1472 for (i=0; i<nprims; i++)
1473 {
1474 p = rmNodeGetPrimitive(node, i);
1475
1476 /* does the primitive have a bounding box? if not, compute one */
1477 if (rmPrimitiveGetBoundingBox(p, &tmin, &tmax) == RM_WHACKED)
1478 {
1479 /* if not, try to compute it. */
1480 if (rmPrimitiveComputeBoundingBox(p) == RM_WHACKED)
1481 {
1482 char buf[256];
1483 sprintf(buf," rmNodeComputeBoundingBox warning - unable to obtain or compute the bounding box for the %d'th RMprimitive. The node's bounding box may be inaccurate. ", i);
1484 rmWarning(buf);
1485
1486 /* bad news - skip this RMprimitive */
1487 continue;
1488 }
1489 else /* grab the newly computed bbox */
1490 rmPrimitiveGetBoundingBox(p, &tmin, &tmax);
1491
1492 haveValidBoxData = RM_TRUE;
1493 }
1494 else
1495 haveValidBoxData = RM_TRUE;
1496
1497 /* perform box union of new and old boxes */
1498 rmUnionBoundingBoxes(&accumMin, &accumMax, &tmin, &tmax, &accumMin, &accumMax);
1499 }
1500
1501 if (haveValidBoxData)
1502 {
1503 rmNodeSetBoundingBox(node, &accumMin, &accumMax);
1504
1505 center.x = (accumMax.x - accumMin.x) * 0.5 + accumMin.x;
1506 center.y = (accumMax.y - accumMin.y) * 0.5 + accumMin.y;
1507 center.z = (accumMax.z - accumMin.z) * 0.5 + accumMin.z;
1508 rmNodeSetCenter(node, ¢er);
1509
1510 return RM_CHILL;
1511 }
1512 else
1513 return RM_WHACKED;
1514
1515 #if 0
1516 /* compute for all prims at node */
1517 for (i = 0; i < nprims; i++)
1518 {
1519
1520 p = rmNodeGetPrimitive(node, i);
1521
1522 if (p == NULL)
1523 continue;
1524
1525 Need to add code that replaces this junk use of existing
1526 RMprimitive bboxes.
1527
1528 switch(rmPrimitiveGetType(p))
1529 {
1530 case RM_SPHERES:
1531 private_computeSpheresBoundingBox(p, &tmin, &tmax);
1532 break; /* end sphere-specific code */
1533
1534 case RM_CYLINDERS:
1535 private_computeCylindersBoundingBox(p, &tmin, &tmax);
1536 break;
1537
1538 default: /* all other prim types */
1539 if (private_computeGenericBoundingBox(p, &tmin, &tmax) == RM_WHACKED)
1540 return(RM_WHACKED);
1541
1542 break;
1543 } /* end switch over prim types */
1544
1545 if (i == 0)
1546 {
1547 accumMin = tmin;
1548 accumMax = tmax;
1549 }
1550 else
1551 rmUnionBoundingBoxes(&accumMin, &accumMax, &tmin, &tmax, &accumMin, &accumMax);
1552
1553 } /* end loop over prims */
1554 rmNodeSetBoundingBox(node, &accumMin, &accumMax);
1555
1556 center.x = (accumMax.x - accumMin.x) * 0.5 + accumMin.x;
1557 center.y = (accumMax.y - accumMin.y) * 0.5 + accumMin.y;
1558 center.z = (accumMax.z - accumMin.z) * 0.5 + accumMin.z;
1559 rmNodeSetCenter(node, ¢er);
1560
1561
1562 return(RM_CHILL);
1563 #endif
1564 }
1565
1566
1567 /*
1568 * ----------------------------------------------------
1569 * @Name rmNodeUnionAllBoxes
1570 @pstart
1571 RMenum rmNodeUnionAllBoxes (RMnode *toModify)
1572 @pend
1573
1574 @astart
1575 RMnode *toModify - a handle to an RMnode (modify).
1576 @aend
1577
1578 @dstart This routine will perform a traversal of the scene graph
1579 rooted at the node toModify, and will perform a "bounding box
1580 union". The union of two bounding boxes can be thought of as the
1581 smallest 3D box that encloses two sub-boxes.
1582
1583 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1584
1585 @dend
1586 * ----------------------------------------------------
1587 */
1588 RMenum
rmNodeUnionAllBoxes(RMnode * n)1589 rmNodeUnionAllBoxes (RMnode *n)
1590 {
1591 int i;
1592 RMenum rstat = RM_CHILL;
1593
1594 if (RM_ASSERT(n, "rmNodeUnionAllBoxes() error: the input RMnode is NULL.") == RM_WHACKED)
1595 return(RM_WHACKED);
1596
1597 for (i = 0; i < n->nchildren; i++)
1598 {
1599 if (n->children[i]->nchildren > 0)
1600 rstat = rmNodeUnionAllBoxes(n->children[i]);
1601
1602 if (rstat == RM_WHACKED)
1603 return(rstat);
1604
1605 rstat = rmUnionBoundingBoxes(&(n->bbox.min), &(n->bbox.max), &(n->children[i]->bbox.min), &(n->children[i]->bbox.max), &(n->bbox.min), &(n->bbox.max));
1606 if (rstat == RM_WHACKED)
1607 return(rstat);
1608 }
1609 return(rstat);
1610 }
1611
1612
1613 /*
1614 * ----------------------------------------------------
1615 * @Name rmNodeSetAmbientColor
1616 @pstart
1617 RMenum rmNodeSetAmbientColor (RMnode *toModify,
1618 const RMcolor4D *newColor)
1619 @pend
1620
1621 @astart
1622 RMnode *toModify - a handle to an RMnode to modify (modified).
1623
1624 const RMcolor4D *newColor - a handle to an RMcolor4D object (input).
1625 @aend
1626
1627 @dstart
1628
1629 Use this routine to set the color of the ambient material reflectence
1630 property at an RMnode. RM_CHILL is returned upon success, or
1631 RM_WHACKED upon failure.
1632
1633 The shade of each vertex (OpenGL supports only vertex shading) is a
1634 function of the Lambertian diffuse shading model, combined with Phong
1635 specular reflection. The weights, or coefficients, applied to each of
1636 the ambient, diffuse and specular components of the shading equation
1637 are the linear combination of ambient, diffuse and specular
1638 coefficients of the surface material properties with the ambient,
1639 diffuse and specular color components of the light sources and light
1640 models.
1641
1642 For lighting/shading to be active, the following three elements must
1643 be present: light sources, light models and surface normals. Light
1644 models and light sources are RMnode-level scene parameters (see
1645 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
1646 normals are RMprimitive attributes. Thus, primitives in 3D are not
1647 considered to be lit (ie, vertex shade computed using light sources
1648 and surface reflectance properties) if normals are not present. When
1649 lighting is not active, the RMnode's "unlit color" attribute is used
1650 for the vertex color.
1651
1652 By default, when an RMnode is created with rmNodeNew(), the new node
1653 contains no surface properties. Surface properties are inherited
1654 along depth in the scene graph. The RM root node has a set of default
1655 material reflectance properties, so application nodes that are
1656 inserted as children of rmRootNode() will inherit those default
1657 material properties.
1658
1659 @dend
1660 * ----------------------------------------------------
1661 */
1662 RMenum
rmNodeSetAmbientColor(RMnode * n,const RMcolor4D * newColor)1663 rmNodeSetAmbientColor (RMnode *n,
1664 const RMcolor4D *newColor)
1665 {
1666 if (RM_ASSERT(n, "rmNodeSetAmbientColor() error: the input RMnode pointer is NULL. ") == RM_WHACKED)
1667 return(RM_WHACKED);
1668
1669 /* if no surface properties are defined, create them */
1670 if (n->sprops == NULL)
1671 n->sprops = private_rmSurfacePropsNew();
1672
1673 /* if no ambient color attribute is present, and the newColor
1674 * isn't NULL, create space for a new 4-tuple color
1675 */
1676 if ((n->sprops->ambient_color == NULL) && (newColor != NULL))
1677 n->sprops->ambient_color = rmColor4DNew(1);
1678
1679 /* if the newColor isn't NULL, copy data into the node */
1680 if (newColor != NULL)
1681 {
1682 *(n->sprops->ambient_color) = *newColor;
1683 }
1684 else
1685 {
1686 /* otherwise, newColor == NULL, and we want to "turn off"
1687 * the ambient color parm at this node. first, we need to
1688 * free up any existing memory
1689 */
1690 if (n->sprops->ambient_color != NULL)
1691 rmColor4DDelete(n->sprops->ambient_color);
1692 n->sprops->ambient_color = NULL;
1693 }
1694 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
1695 return(RM_CHILL);
1696 }
1697
1698
1699 /*
1700 * ----------------------------------------------------
1701 * @Name rmNodeGetAmbientColor
1702 @pstart
1703 RMenum rmNodeGetAmbientColor (const RMnode *toQuery,
1704 RMcolor4D *ambientReturn)
1705 @pend
1706
1707 @astart
1708 const RMnode *toQuery - a handle to an RMnode (input).
1709
1710 RMcolor4D *ambientReturn - a handle to a caller-supplied RMcolor4D
1711 object (modified).
1712 @aend
1713
1714 @dstart
1715
1716 Use this routine to obtain the 4-component ambient reflectance color
1717 attribute of an RMnode. Upon success, the ambient reflectance color
1718 attribute of an RMnode will be copied into the caller-supplied
1719 memory, and RM_CHILL will be returned.
1720
1721 If the input RMnode is NULL, or if the ambient reflectance color is
1722 not defined at an RMnode, RM_WHACKED is returned to the caller.
1723
1724 The shade of each vertex (OpenGL supports only vertex shading) is a
1725 function of the Lambertian diffuse shading model, combined with Phong
1726 specular reflection. The weights, or coefficients, applied to each of
1727 the ambient, diffuse and specular components of the shading equation
1728 are the linear combination of ambient, diffuse and specular
1729 coefficients of the surface material properties with the ambient,
1730 diffuse and specular color components of the light sources and light
1731 models.
1732
1733 For lighting/shading to be active, the following three elements must
1734 be present: light sources, light models and surface normals. Light
1735 models and light sources are RMnode-level scene parameters (see
1736 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
1737 normals are RMprimitive attributes. Thus, primitives in 3D are not
1738 considered to be lit (ie, vertex shade computed using light sources
1739 and surface reflectance properties) if no normals are present. When
1740 lighting is not active, the RMnode's "unlit color" attribute is used
1741 for the vertex color.
1742
1743 By default, when an RMnode is created with rmNodeNew(), the new node
1744 contains no surface properties. Surface properties are inherited
1745 along depth in the scene graph. The RM root node has a set of default
1746 material reflectance properties, so application nodes that are
1747 inserted as children of rmRootNode() will inherit those default
1748 material properties.
1749
1750 @dend
1751 * ----------------------------------------------------
1752 */
1753 RMenum
rmNodeGetAmbientColor(const RMnode * n,RMcolor4D * ar)1754 rmNodeGetAmbientColor (const RMnode *n,
1755 RMcolor4D *ar)
1756 {
1757 if ((RM_ASSERT(n, "rmNodeGetAmbientColor() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
1758 (RM_ASSERT(ar, "rmNodeGetAmbientColor() error: the RMcolor4D pointer is NULL")) == RM_WHACKED)
1759 return(RM_WHACKED);
1760
1761 if ((n->sprops == NULL) || (n->sprops->ambient_color == NULL))
1762 return(RM_WHACKED);
1763
1764 *ar = *(n->sprops->ambient_color);
1765
1766 return(RM_CHILL);
1767 }
1768
1769
1770 /*
1771 * ----------------------------------------------------
1772 * @Name rmNodeSetDiffuseColor
1773 @pstart
1774 RMenum rmNodeSetDiffuseColor (RMnode *toModify,
1775 const RMcolor4D *newColor)
1776 @pend
1777
1778 @astart
1779 RMnode *toModify - a handle to an RMnode to modify (modified).
1780
1781 const RMcolor4D *newColor - a handle to an RMcolor4D object (input).
1782 @aend
1783
1784 @dstart
1785
1786 Use this routine to set the color of the diffuse material reflectence
1787 property at an RMnode. RM_CHILL is returned upon success, or
1788 RM_WHACKED upon failure.
1789
1790 The shade of each vertex (OpenGL supports only vertex shading) is a
1791 function of the Lambertian diffuse shading model, combined with Phong
1792 specular reflection. The weights, or coefficients, applied to each of
1793 the ambient, diffuse and specular components of the shading equation
1794 are the linear combination of ambient, diffuse and specular
1795 coefficients of the surface material properties with the ambient,
1796 diffuse and specular color components of the light sources and light
1797 models.
1798
1799 For lighting/shading to be active, the following three elements must
1800 be present: light sources, light models and surface normals. Light
1801 models and light sources are RMnode-level scene parameters (see
1802 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
1803 normals are RMprimitive attributes. Thus, primitives in 3D are not
1804 considered to be lit (ie, vertex shade computed using light sources
1805 and surface reflectance properties) if no normals are present. When
1806 lighting is not active, the RMnode's "unlit color" attribute is used
1807 for the vertex color.
1808
1809 By default, when an RMnode is created with rmNodeNew(), the new node
1810 contains no surface properties. Surface properties are inherited
1811 along depth in the scene graph. The RM root node has a set of default
1812 material reflectance properties, so application nodes that are
1813 inserted as children of rmRootNode() will inherit those default
1814 material properties.
1815
1816 @dend
1817 * ----------------------------------------------------
1818 */
1819 RMenum
rmNodeSetDiffuseColor(RMnode * n,const RMcolor4D * newColor)1820 rmNodeSetDiffuseColor (RMnode *n,
1821 const RMcolor4D *newColor)
1822 {
1823 if (RM_ASSERT(n, "rmNodeSetDiffuseColor() error: the input RMnode pointer is NULL. ") == RM_WHACKED)
1824 return(RM_WHACKED);
1825
1826 /* if no surface properties are defined, create them */
1827 if (n->sprops == NULL)
1828 n->sprops = private_rmSurfacePropsNew();
1829
1830 /* if no diffuse color attribute is present, and the newColor
1831 * isn't NULL, create space for a new 4-tuple color
1832 */
1833 if ((n->sprops->diffuse_color == NULL) && (newColor != NULL))
1834 n->sprops->diffuse_color = rmColor4DNew(1);
1835
1836 /* if the newColor isn't NULL, copy data into the node */
1837 if (newColor != NULL)
1838 {
1839 *(n->sprops->diffuse_color) = *newColor;
1840 }
1841 else
1842 {
1843 /* otherwise, newColor == NULL, and we want to "turn off"
1844 * the diffuse color parm at this node. first, we need to
1845 * free up any existing memory
1846 */
1847 if (n->sprops->diffuse_color != NULL)
1848 rmColor4DDelete(n->sprops->diffuse_color);
1849 n->sprops->diffuse_color = NULL;
1850 }
1851 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
1852 return(RM_CHILL);
1853 }
1854
1855
1856 /*
1857 * ----------------------------------------------------
1858 * @Name rmNodeGetDiffuseColor
1859 @pstart
1860 RMenum rmNodeGetDiffuseColor (const RMnode *toQuery,
1861 RMcolor4D *diffuseReturn)
1862 @pend
1863
1864 @astart
1865 const RMnode *toQuery - a handle to an RMnode (input).
1866
1867 RMcolor4D *diffuseReturn - a handle to a caller-supplied RMcolor4D
1868 object (modified).
1869 @aend
1870
1871 @dstart
1872
1873 Use this routine to obtain the 4-component diffuse reflectance color
1874 attribute of an RMnode. Upon success, the diffuse reflectance color
1875 attribute of an RMnode will be copied into the caller-supplied
1876 memory, and RM_CHILL will be returned.
1877
1878 If the input RMnode is NULL, or if the diffuse reflectance color is
1879 not defined at an RMnode, RM_WHACKED is returned to the caller.
1880
1881 The shade of each vertex (OpenGL supports only vertex shading) is a
1882 function of the Lambertian diffuse shading model, combined with Phong
1883 specular reflection. The weights, or coefficients, applied to each of
1884 the ambient, diffuse and specular components of the shading equation
1885 are the linear combination of ambient, diffuse and specular
1886 coefficients of the surface material properties with the ambient,
1887 diffuse and specular color components of the light sources and light
1888 models.
1889
1890 For lighting/shading to be active, the following three elements must
1891 be present: light sources, light models and surface normals. Light
1892 models and light sources are RMnode-level scene parameters (see
1893 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
1894 normals are RMprimitive attributes. Thus, primitives in 3D are not
1895 considered to be lit (ie, vertex shade computed using light sources
1896 and surface reflectance properties) if no normals are present. When
1897 lighting is not active, the RMnode's "unlit color" attribute is used
1898 for the vertex color.
1899
1900 By default, when an RMnode is created with rmNodeNew(), the new node
1901 contains no surface properties. Surface properties are inherited
1902 along depth in the scene graph. The RM root node has a set of default
1903 material reflectance properties, so application nodes that are
1904 inserted as children of rmRootNode() will inherit those default
1905 material properties.
1906
1907 @dend
1908 * ----------------------------------------------------
1909 */
1910 RMenum
rmNodeGetDiffuseColor(const RMnode * n,RMcolor4D * dr)1911 rmNodeGetDiffuseColor (const RMnode *n,
1912 RMcolor4D *dr)
1913 {
1914 if ((RM_ASSERT(n, "rmNodeGetDiffuseColor() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
1915 (RM_ASSERT(dr, "rmNodeGetDiffuseColor() error: the RMcolor4D pointer is NULL")) == RM_WHACKED)
1916 return(RM_WHACKED);
1917
1918 if ((n->sprops == NULL) || (n->sprops->diffuse_color == NULL))
1919 return(RM_WHACKED);
1920
1921 *dr = *(n->sprops->diffuse_color);
1922
1923 return(RM_CHILL);
1924 }
1925
1926
1927 /*
1928 * ----------------------------------------------------
1929 * @Name rmNodeSetSpecularColor
1930 @pstart
1931 RMenum rmNodeSetSpecularColor (RMnode *toModify,
1932 const RMcolor4D *newColor)
1933 @pend
1934
1935 @astart
1936 RMnode *toModify - a handle to an RMnode to modify (modified).
1937
1938 const RMcolor4D *newColor - a handle to an RMcolor4D object (input).
1939 @aend
1940
1941 @dstart
1942
1943 Use this routine to set the color of the specular material
1944 reflectence property at an RMnode. RM_CHILL is returned upon success,
1945 or RM_WHACKED upon failure.
1946
1947 The shade of each vertex (OpenGL supports only vertex shading) is a
1948 function of the Lambertian diffuse shading model, combined with Phong
1949 specular reflection. The weights, or coefficients, applied to each of
1950 the ambient, diffuse and specular components of the shading equation
1951 are the linear combination of ambient, diffuse and specular
1952 coefficients of the surface material properties with the ambient,
1953 diffuse and specular color components of the light sources and light
1954 models.
1955
1956 For lighting/shading to be active, the following three elements must
1957 be present: light sources, light models and surface normals. Light
1958 models and light sources are RMnode-level scene parameters (see
1959 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
1960 normals are RMprimitive attributes. Thus, primitives in 3D are not
1961 considered to be lit (ie, vertex shade computed using light sources
1962 and surface reflectance properties) if no normals are present. When
1963 lighting is not active, the RMnode's "unlit color" attribute is used
1964 for the vertex color.
1965
1966 By default, when an RMnode is created with rmNodeNew(), the new node
1967 contains no surface properties. Surface properties are inherited
1968 along depth in the scene graph. The RM root node has a set of default
1969 material reflectance properties, so application nodes that are
1970 inserted as children of rmRootNode() will inherit those default
1971 material properties.
1972
1973 @dend
1974 * ----------------------------------------------------
1975 */
1976 RMenum
rmNodeSetSpecularColor(RMnode * n,const RMcolor4D * newColor)1977 rmNodeSetSpecularColor (RMnode *n,
1978 const RMcolor4D *newColor)
1979 {
1980 if (RM_ASSERT(n, "rmNodeSetSpecularColor() error: the input RMnode pointer is NULL. ") == RM_WHACKED)
1981 return(RM_WHACKED);
1982
1983 /* if no surface properties are defined, create them */
1984 if (n->sprops == NULL)
1985 n->sprops = private_rmSurfacePropsNew();
1986
1987 /* if no specular color attribute is present, and the newColor
1988 * isn't NULL, create space for a new 4-tuple color
1989 */
1990 if ((n->sprops->specular_color == NULL) && (newColor != NULL))
1991 n->sprops->specular_color = rmColor4DNew(1);
1992
1993 /* if the newColor isn't NULL, copy data into the node */
1994 if (newColor != NULL)
1995 {
1996 *(n->sprops->specular_color) = *newColor;
1997 }
1998 else
1999 {
2000 /* otherwise, newColor == NULL, and we want to "turn off"
2001 * the specular color parm at this node. first, we need to
2002 * free up any existing memory
2003 */
2004 if (n->sprops->specular_color != NULL)
2005 rmColor4DDelete(n->sprops->specular_color);
2006
2007 n->sprops->specular_color = NULL;
2008 }
2009 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2010 return(RM_CHILL);
2011 }
2012
2013
2014 /*
2015 * ----------------------------------------------------
2016 * @Name rmNodeGetSpecularColor
2017 @pstart
2018 RMenum rmNodeGetSpecularColor (const RMnode *toQuery,
2019 RMcolor4D *diffuseReturn)
2020 @pend
2021
2022 @astart
2023 const RMnode *toQuery - a handle to an RMnode (input).
2024
2025 RMcolor4D *specularReturn - a handle to a caller-supplied RMcolor4D
2026 object (modified).
2027 @aend
2028
2029 @dstart
2030
2031 Use this routine to obtain the 4-component specular reflectance color
2032 attribute of an RMnode. Upon success, the specular reflectance color
2033 attribute of an RMnode will be copied into the caller-supplied
2034 memory, and RM_CHILL will be returned.
2035
2036 If the input RMnode is NULL, or if the specular reflectance color is
2037 not defined at an RMnode, RM_WHACKED is returned to the caller.
2038
2039 The shade of each vertex (OpenGL supports only vertex shading) is a
2040 function of the Lambertian diffuse shading model, combined with Phong
2041 specular reflection. The weights, or coefficients, applied to each of
2042 the ambient, diffuse and specular components of the shading equation
2043 are the linear combination of ambient, diffuse and specular
2044 coefficients of the surface material properties with the ambient,
2045 diffuse and specular color components of the light sources and light
2046 models.
2047
2048 For lighting/shading to be active, the following three elements must
2049 be present: light sources, light models and surface normals. Light
2050 models and light sources are RMnode-level scene parameters (see
2051 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
2052 normals are RMprimitive attributes. Thus, primitives in 3D are not
2053 considered to be lit (ie, vertex shade computed using light sources
2054 and surface reflectance properties) if no normals are present. When
2055 lighting is not active, the RMnode's "unlit color" attribute is used
2056 for the vertex color.
2057
2058 By default, when an RMnode is created with rmNodeNew(), the new node
2059 contains no surface properties. Surface properties are inherited
2060 along depth in the scene graph. The RM root node has a set of default
2061 material reflectance properties, so application nodes that are
2062 inserted as children of rmRootNode() will inherit those default
2063 material properties.
2064
2065 @dend
2066 * ----------------------------------------------------
2067 */
2068 RMenum
rmNodeGetSpecularColor(const RMnode * n,RMcolor4D * sr)2069 rmNodeGetSpecularColor (const RMnode *n,
2070 RMcolor4D *sr)
2071 {
2072 if ((RM_ASSERT(n, "rmNodeGetSpecularColor() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
2073 (RM_ASSERT(sr, "rmNodeGetSpecularColor() error: the RMcolor4D pointer is NULL")) == RM_WHACKED)
2074 return(RM_WHACKED);
2075
2076 if ((n->sprops == NULL) || (n->sprops->specular_color == NULL))
2077 return(RM_WHACKED);
2078
2079 *sr = *(n->sprops->specular_color);
2080
2081 return(RM_CHILL);
2082 }
2083
2084
2085 /*
2086 * ----------------------------------------------------
2087 * @Name rmNodeSetSpecularExponent
2088 @pstart
2089 RMenum rmNodeSetSpecularExponent (RMnode *toModify,
2090 float newValue)
2091 @pend
2092
2093 @astart
2094 RMnode *toModify - a handle to an RMnode (modified).
2095
2096 float newValue - a floating point value, typically greater than 1.0
2097 (input).
2098 @aend
2099
2100 @dstart
2101
2102 Use this routine to set the specular reflectence exponent surface
2103 reflectance material property. RM_CHILL is returned upon success, or
2104 RM_WHACKED upon failure.
2105
2106 The shade of each vertex (OpenGL supports only vertex shading) is a
2107 function of the Lambertian diffuse shading model, combined with Phong
2108 specular reflection. The weights, or coefficients, applied to each of
2109 the ambient, diffuse and specular components of the shading equation
2110 are the linear combination of ambient, diffuse and specular
2111 coefficients of the surface material properties with the ambient,
2112 diffuse and specular color components of the light sources and light
2113 models.
2114
2115 The specular exponent controls the appearance of specular highlights.
2116 A smaller exponent value produces a larger specular highlight, while
2117 a larger exponent value produces a smaller, more focused highlight.
2118 The default specular exponent in RM is 10.0.
2119
2120 For lighting/shading to be active, the following three elements must
2121 be present: light sources, light models and surface normals. Light
2122 models and light sources are RMnode-level scene parameters (see
2123 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
2124 normals are RMprimitive attributes. Thus, primitives in 3D are not
2125 considered to be lit (ie, vertex shade computed using light sources
2126 and surface reflectance properties) if no normals are present. When
2127 lighting is not active, the RMnode's "unlit color" attribute is used
2128 for the vertex color.
2129
2130 By default, when an RMnode is created with rmNodeNew(), the new node
2131 contains no surface properties. Surface properties are inherited
2132 along depth in the scene graph. The RM root node has a set of default
2133 material reflectance properties, so application nodes that are
2134 inserted as children of rmRootNode() will inherit those default
2135 material properties.
2136
2137 @dend
2138 * ----------------------------------------------------
2139 */
2140 RMenum
rmNodeSetSpecularExponent(RMnode * n,float newValue)2141 rmNodeSetSpecularExponent (RMnode *n, float newValue)
2142 {
2143 if (RM_ASSERT(n, "rmNodeSetSpecularExponent() error: the input RMnode is NULL") == RM_WHACKED)
2144 return(RM_WHACKED);
2145
2146 /* if no surface properties are defined, create them */
2147 if (n->sprops == NULL)
2148 n->sprops = private_rmSurfacePropsNew();
2149
2150 if (n->sprops->specular_exponent == NULL)
2151 n->sprops->specular_exponent = rmFloatNew(1);
2152
2153 *(n->sprops->specular_exponent) = newValue;
2154
2155 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2156 return(RM_CHILL);
2157 }
2158
2159
2160 /*
2161 * ----------------------------------------------------
2162 * @Name rmNodeGetSpecularExponent
2163 @pstart
2164 RMenum rmNodeGetSpecularExponent (const RMnode *toQuery,
2165 float *retValue)
2166 @pend
2167
2168 @astart
2169 const RMnode *toQuery - a handle to an RMnode to query (input).
2170
2171 float *retValue - a handle to a caller-supplied float (modified).
2172 @aend
2173
2174 @dstart
2175
2176 Use this routine to obtain the specular exponent material reflectance
2177 attribute of an RMnode. Upon success, RM_CHILL is returned, and the
2178 RMnode's specular exponent is copied into caller-supplied memory.
2179
2180 If the input node is NULL, or if the input node has no surface
2181 reflectance properties, or if the specular exponent term has not been
2182 defined (through explicit assignment using
2183 rmNodeSetSpecularExponent()), RM_WHACKED is returned and caller
2184 memory remains undisturbed.
2185
2186 @dend
2187 * ----------------------------------------------------
2188 */
2189 RMenum
rmNodeGetSpecularExponent(const RMnode * n,float * float_ret)2190 rmNodeGetSpecularExponent (const RMnode *n,
2191 float *float_ret)
2192 {
2193 if ((n->sprops == NULL) || (n->sprops->specular_exponent == NULL))
2194 return(RM_WHACKED);
2195
2196 *float_ret = *(n->sprops->specular_exponent);
2197
2198 return(RM_CHILL);
2199 }
2200
2201
2202 /*
2203 * ----------------------------------------------------
2204 * @Name rmNodeSetUnlitColor
2205 @pstart
2206 RMenum rmNodeSetUnlitColor (RMnode *toModify,
2207 const RMcolor4D *newColor)
2208 @pend
2209
2210 @astart
2211 RMnode *toModify - a handle to an RMnode (modified).
2212
2213 const RMcolor4D *newColor - a handle to an RMcolor4D object (input).
2214 @aend
2215
2216 @dstart
2217
2218 Use this routine to set the "unlit" material color of an RMnode.
2219 RM_CHILL is returned upon success, or RM_WHACKED upon failure.
2220
2221 The term "unlit color" refers to the color used to draw primitives
2222 when lighting is not active. Rather than arbitrarily choose from
2223 among diffuse, ambient or specular material colors for use when
2224 rendering in the absence of lights, RM provides a node-level
2225 attribute for that purpose: unlit color. A newColor value of (0,0,0,1)
2226 specifies the color "black," while a newColor value of (1,1,1,1)
2227 specifies the color "white."
2228
2229 For lighting/shading to be active, the following three elements must
2230 be present: light sources, light models and surface normals. Light
2231 models and light sources are RMnode-level scene parameters (see
2232 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
2233 normals are RMprimitive attributes. Thus, primitives in 3D are not
2234 considered to be lit (ie, vertex shade computed using light sources
2235 and surface reflectance properties) if no normals are present. When
2236 lighting is not active, the RMnode's "unlit color" attribute is used
2237 for the vertex color.
2238
2239 By default, when an RMnode is created with rmNodeNew(), the new node
2240 contains no surface properties. Surface properties are inherited
2241 along depth in the scene graph. The RM root node has a set of default
2242 material reflectance properties, so application nodes that are
2243 inserted as children of rmRootNode() will inherit those default
2244 material properties.
2245
2246 @dend
2247 * ----------------------------------------------------
2248 */
2249 RMenum
rmNodeSetUnlitColor(RMnode * n,const RMcolor4D * newColor)2250 rmNodeSetUnlitColor (RMnode *n,
2251 const RMcolor4D *newColor)
2252 {
2253 if (RM_ASSERT(n, "rmNodeSetUnlitColor() error: the input RMnode pointer is NULL. ") == RM_WHACKED)
2254 return(RM_WHACKED);
2255
2256 /* if no surface properties are defined, create them */
2257 if (n->sprops == NULL)
2258 n->sprops = private_rmSurfacePropsNew();
2259
2260 /* if no unlit color attribute is present, and the newColor
2261 * isn't NULL, create space for a new 4-tuple color
2262 */
2263 if ((n->sprops->unlit_color == NULL) && (newColor != NULL))
2264 n->sprops->unlit_color = rmColor4DNew(1);
2265
2266 /* if the newColor isn't NULL, copy data into the node */
2267 if (newColor != NULL)
2268 {
2269 *(n->sprops->unlit_color) = *newColor;
2270 }
2271 else
2272 {
2273 /* otherwise, newColor == NULL, and we want to "turn off"
2274 * the unlit color parm at this node. first, we need to
2275 * free up any existing memory
2276 */
2277 if (n->sprops->unlit_color != NULL)
2278 rmColor4DDelete(n->sprops->unlit_color);
2279
2280 n->sprops->unlit_color = NULL;
2281 }
2282 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2283 return(RM_CHILL);
2284 }
2285
2286
2287 /*
2288 * ----------------------------------------------------
2289 * @Name rmNodeGetUnlitColor
2290 @pstart
2291 RMenum rmNodeGetUnlitColor (const RMnode *toQuery,
2292 RMcolor4D *retColor)
2293 @pend
2294
2295 @astart
2296 const RMnode *toModify - a handle to an RMnode (input).
2297
2298 RMcolor4D *retColor - a handle to an RMcolor4D object (modified).
2299 @aend
2300
2301 @dstart
2302
2303 Use this routine to obtain the "unlit" material color of an RMnode.
2304 Upon success, RM_CHILL is returned and the RMnode's unlit color
2305 attribute is copied into caller-supplied memory. If, however, the
2306 RMnode has no surface reflectance properties defined, or if the unlit
2307 color attribute has not been explicitly set (with
2308 rmNodeSetUnlitColor), RM_WHACKED is returned, and caller memory
2309 remains unmodified.
2310
2311 The term "unlit color" refers to the color used to draw primitives
2312 when lighting is not active. Rather than arbitrarily choose from
2313 among diffuse, ambient or specular material colors for use when
2314 rendering in the absence of lights, RM provides a node-level
2315 attribute for that purpose: unlit color.
2316
2317 For lighting/shading to be active, the following three elements must
2318 be present: light sources, light models and surface normals. Light
2319 models and light sources are RMnode-level scene parameters (see
2320 rmNodeSetSceneLight() and rmNodeSetSceneLightModel()). Surface
2321 normals are RMprimitive attributes. Thus, primitives in 3D are not
2322 considered to be lit (ie, vertex shade computed using light sources
2323 and surface reflectance properties) if no normals are present. When
2324 lighting is not active, the RMnode's "unlit color" attribute is used
2325 for the vertex color.
2326
2327 By default, when an RMnode is created with rmNodeNew(), the new node
2328 contains no surface properties. Surface properties are inherited
2329 along depth in the scene graph. The RM root node has a set of default
2330 material reflectance properties, so application nodes that are
2331 inserted as children of rmRootNode() will inherit those default
2332 material properties.
2333
2334 @dend
2335 * ----------------------------------------------------
2336 */
2337 RMenum
rmNodeGetUnlitColor(const RMnode * n,RMcolor4D * ur)2338 rmNodeGetUnlitColor (const RMnode *n,
2339 RMcolor4D *ur)
2340 {
2341 if ((RM_ASSERT(n, "rmNodeGetUnlitColor() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
2342 (RM_ASSERT(ur, "rmNodeGetUnlitColor() error: the RMcolor4D pointer is NULL")) == RM_WHACKED)
2343 return(RM_WHACKED);
2344
2345 if ((n->sprops == NULL) || (n->sprops->unlit_color == NULL))
2346 return(RM_WHACKED);
2347
2348 *ur = *(n->sprops->unlit_color);
2349
2350 return(RM_CHILL);
2351 }
2352
2353
2354 /*
2355 * ----------------------------------------------------
2356 * @Name rmNodeSetOpacity
2357 @pstart
2358 RMenum rmNodeSetOpacity (RMnode *toModify,
2359 float newValue)
2360 @pend
2361
2362 @astart
2363 RMnode *toModify - a handle to an RMnode (modified).
2364
2365 float newValue - a floating point value between 0.0 and 1.0. A value
2366 of 0.0 means the object is completely transparent, while a value
2367 of 1.0 means the object is completely opaque (input).
2368 @aend
2369
2370 @dstart
2371
2372 Use this routine to set the opacity level for all RMprimitives in an
2373 RMnode. RM_CHILL is returned upon success, or RM_WHACKED upon
2374 failure.
2375
2376 In order for objects to be rendered as translucent, a value of
2377 opacity other than 1.0 must be specified, either at the RMnode level
2378 or at the RMprimitive level. In the former case, the opacity value
2379 applies to all RMprimitives in an RMnode (and all descendent
2380 primitives, unless subsequently overridden by the presence of the
2381 node-level opacity value or RMprimitive level opacities). In the
2382 latter case, opacity is specified on a per-vertex level. In addition,
2383 the RMnode must be scheduled for traversal during the transparent
2384 rendering pass. See rmNodeNew() for more details.
2385
2386 By default, when an RMnode is created with rmNodeNew(), the new node
2387 contains no surface properties. Surface properties are inherited
2388 along depth in the scene graph. The RM root node has a set of default
2389 material reflectance properties, so application nodes that are
2390 inserted as children of rmRootNode() will inherit those default
2391 material properties.
2392
2393 @dend
2394 * ----------------------------------------------------
2395 */
2396 RMenum
rmNodeSetOpacity(RMnode * n,float newValue)2397 rmNodeSetOpacity (RMnode *n, float newValue)
2398 {
2399 rmWarning(" rmNodeSetOpacity() is deprecated. Please set a node's opacity through manip of its unlit color, or one or more of its material properties. rmNodeSetOpacity() will be removed from the API in the 1.4.3 release. ");
2400
2401 if (RM_ASSERT(n, "rmNodeSetOpacity() error: the input RMnode is NULL.") == RM_WHACKED)
2402 return(RM_WHACKED);
2403
2404 if (n->sprops == NULL)
2405 n->sprops = private_rmSurfacePropsNew();
2406
2407 if (n->sprops->opacity == NULL)
2408 n->sprops->opacity = rmFloatNew(1);
2409
2410 *(n->sprops->opacity) = newValue;
2411
2412 return(RM_CHILL);
2413 }
2414
2415
2416 /*
2417 * ----------------------------------------------------
2418 * @Name rmNodeGetOpacity
2419 @pstart
2420 RMenum rmNodeGetOpacity (const RMnode *toQuery,
2421 float *retValue)
2422 @pend
2423
2424 @astart
2425 RMnode *toQuery - a handle to an RMnode (input).
2426
2427 float *retValue - a handle to a caller-supplied float (modified).
2428 @aend
2429
2430 @dstart
2431
2432 Use this routine to obtain the opacity attribute of an RMnode. Upon
2433 success, RM_CHILL is returned, and the RMnode's opacity attribute is
2434 copied into caller-supplied memory. If the RMnode is NULL, or if the
2435 RMnode has no surface reflectance properties, or if the opacity
2436 attribute is not present (specified with rmNodeSetOpacity()),
2437 RM_WHACKED is returned and caller-supplied memory remains
2438 undisturbed.
2439
2440 @dend
2441 * ----------------------------------------------------
2442 */
2443 RMenum
rmNodeGetOpacity(const RMnode * n,float * float_ret)2444 rmNodeGetOpacity (const RMnode *n,
2445 float *float_ret)
2446 {
2447 rmWarning(" rmNodeGetOpacity() is deprecated, and will be removed from the API in the 1.4.3 release. ");
2448
2449 if ((RM_ASSERT(n,"rmNodeGetOpacity() error: the input RMnode is NULL") == RM_WHACKED) ||
2450 (RM_ASSERT(float_ret,"rmNodeGetOpacity() error: the return floating point parameter is NULL") == RM_WHACKED))
2451 return(RM_WHACKED);
2452
2453 if ((n->sprops == NULL) || (n->sprops->opacity==NULL))
2454 return(RM_WHACKED);
2455
2456 float_ret = n->sprops->opacity;
2457
2458 return(RM_CHILL);
2459 }
2460
2461 /* drawing attributes */
2462 /*
2463 * ----------------------------------------------------
2464 * @Name rmNodeSetNormalizeNormals
2465 @pstart
2466 RMenum rmNodeSetNormalizeNormals(RMnode *toModify,
2467 RMenum newValue)
2468 @pend
2469
2470 @astart
2471 RMnode *toModify - a handle to an RMnode (modified).
2472 RMenum newValue - an RMenum value, may be either RM_TRUE or RM_FALSE (input).
2473 @aend
2474
2475 @dstart
2476 By default, surface normals are not "auto-normalized" during rendering.
2477 In some circumstances, it is desireable to ask OpenGL to normalize
2478 surface normals prior to rendering, most notably when the governing
2479 transformation matrix includes a scaling component. The effect of
2480 scaled normals will appear on-screen as objects becoming brigher or
2481 dimmer as they are scaled. Enabling auto-normalization will remedy
2482 this artifact, but at the expense of speed.
2483
2484 This routine is used to enable or disable auto-normalization of surface
2485 normals during rendering. Setting this parameter at an RMnode will either
2486 enable or disable normalization of surface normals at render time for
2487 the entire scene graph rooted at the RMnode "toModify."
2488
2489 Upon success, this routine will return RM_CHILL, and modify the RMnode's
2490 drawing attributes accordingly. Otherwise, RM_WHACKED is returned, and
2491 the RMnode's drawing attributes remain unmodified.
2492
2493 Use rmNodeGetNormalizeNormals to query this parameter.
2494
2495 Note that there is not means at this time (3/2000) to obtain from
2496 the RM rendering context (RMstate object) any indication of whether
2497 or not normals are normalized. This may be added in a future release.
2498 @dend
2499 * ----------------------------------------------------
2500 */
2501 RMenum
rmNodeSetNormalizeNormals(RMnode * toModify,RMenum newValue)2502 rmNodeSetNormalizeNormals (RMnode *toModify,
2503 RMenum newValue)
2504 {
2505 RMnode *n;
2506
2507 if (RM_ASSERT(toModify, "rmNodeSetNormalizeNormals() error: the input RMnode is NULL") == RM_WHACKED)
2508 return(RM_WHACKED);
2509
2510 if ((newValue != RM_TRUE) && (newValue != RM_FALSE))
2511 {
2512 rmError("rmNodeSetNormalizeNormals() error: the input RMenum is neither RM_TRUE nor RM_FALSE");
2513 return(RM_WHACKED);
2514 }
2515
2516 n = toModify;
2517
2518 if (n->rprops == NULL)
2519 n->rprops = (_rendermode_properties *)private_rmRenderModePropsNew();
2520
2521 if (n->rprops->normalizeNormals == NULL)
2522 n->rprops->normalizeNormals = private_rmEnumNew(1);
2523
2524 (*n->rprops->normalizeNormals) = newValue;
2525
2526 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2527 return(RM_CHILL);
2528 }
2529
2530 /*
2531 * ----------------------------------------------------
2532 * @Name rmNodeGetNormalizeNormals
2533 @pstart
2534 RMenum rmNodeGetNormalizeNormals(const RMnode *toQuery,
2535 RMenum *valueReturn)
2536 @pend
2537
2538 @astart
2539 const RMnode *toQuery - a handle to an RMnode (input).
2540 RMenum *valueReturn - a handle to a caller-supplied RMenum (result).
2541 @aend
2542
2543 @dstart
2544 Use this routine to query the "normals are normalized" drawing
2545 attribute of an RMnode. If the RMnode has this parameter defined,
2546 RM_CHILL is returned, and the parameter is copied into caller supplied
2547 memory (if the parameter is non-NULL). Otherwise, RM_WHACKED is returned
2548 and caller-supplied memory remains unmodified.
2549
2550 See rmNodeSetNormalizeNormals() for more information.
2551 @dend
2552 * ----------------------------------------------------
2553 */
2554 RMenum
rmNodeGetNormalizeNormals(const RMnode * n,RMenum * rval)2555 rmNodeGetNormalizeNormals (const RMnode *n,
2556 RMenum *rval)
2557 {
2558 if (RM_ASSERT(n, "rmNodeGetNormalizeNormals() error: input RMnode is NULL. ") == RM_WHACKED)
2559 return(RM_WHACKED);
2560
2561 if ((n->rprops == NULL) || (n->rprops->normalizeNormals == NULL))
2562 return(RM_WHACKED);
2563
2564 *rval = *(n->rprops->normalizeNormals);
2565
2566 return(RM_CHILL);
2567 }
2568
2569
2570 /*
2571 * ----------------------------------------------------
2572 * @Name rmNodeSetLineStyle
2573 @pstart
2574 RMenum rmNodeSetLineStyle(RMnode *toModify,
2575 RMenum newLineStyle)
2576 @pend
2577
2578 @astart
2579 RMnode *toModify - a handle to an RMnode (modified).
2580 RMenum newLineStyle - an RMenum value, may be one of RM_LINES_SOLID,
2581 RM_LINES_DASHED, RM_LINES_DOTTED, RM_LINES_DOT_DASH or
2582 RM_LINES_DASH_DASH_DOT (input).
2583 @aend
2584
2585 @dstart
2586 Line styles in RM control the appearance of lines when rendered. Lines
2587 can be rendered as solid lines, or with using a pattern. This routine is
2588 used to manipulate the pattern used for line rendering using on of a
2589 predefined set of styles. Please refer to the program "lines2d" in the
2590 RM demonstration programs for a visual representation of available line
2591 styles and widths.
2592
2593 Upon success, the line style attribute is set to the value specified by
2594 the "newLineStyle" parameter, and RM_CHILL is returned. Upon failure,
2595 RM_WHACKED is returned and the line style attribute of the RMnode
2596 "toModify" remains unmodified.
2597
2598 Use rmNodeGetLineStyle() to obtain the line style attribute of an RMnode,
2599 or rmStateGetLineStyle() to obtain the current line style attribute from
2600 an RM rendering context during a render time traversal of the scene graph.
2601
2602 All RMnode-level drawing properties and modes take effect immediately when
2603 the RMnode is is processed during a render time traversal of the scene graph.
2604 These settings remain in effect over all children of the RMnode, unless
2605 overridden by some child node deeper in the scene graph.
2606 @dend
2607 * ----------------------------------------------------
2608 */
2609 RMenum
rmNodeSetLineStyle(RMnode * n,RMenum s)2610 rmNodeSetLineStyle(RMnode *n,
2611 RMenum s)
2612 {
2613 if (RM_ASSERT(n,"rmNodeSetLineStyle() error: input RMnode is NULL.") == RM_WHACKED)
2614 return(RM_WHACKED);
2615
2616 /* enum check on style enum? */
2617
2618 if (n->rprops == NULL)
2619 n->rprops = private_rmRenderModePropsNew();
2620
2621 if (n->rprops->linestyle == NULL)
2622 n->rprops->linestyle = private_rmEnumNew(1);
2623
2624 *(n->rprops->linestyle) = s;
2625
2626 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2627 return(RM_CHILL);
2628 }
2629
2630
2631 /*
2632 * ----------------------------------------------------
2633 * @Name rmNodeGetLineStyle
2634 @pstart
2635 RMenum rmNodeGetLineStyle(const RMnode *toQuery,
2636 RMenum *lineStyleReturn)
2637 @pend
2638
2639 @astart
2640 const RMnode *toQuery - a handle to an RMnode (input).
2641 RMenum *lineStyleReturn - a handle to a caller supplied RMenum (result).
2642 @aend
2643
2644 @dstart
2645 Use this routine to query the line style attribute of an RMnode. If one is
2646 defined, RM_CHILL is returned on the stack, and the RMnode's line style
2647 attribute will be copied into caller-supplied memory. Otherwise, RM_WHACKED
2648 is returned. Please refer to rmNodeSetLineStyle() for details about
2649 line style enumerators.
2650
2651 To obtain the render-time value of the line width parameter of the
2652 RM rendering context, use rmStateGetLineStyle() from inside an application
2653 callback.
2654 @dend
2655 * ----------------------------------------------------
2656 */
2657 RMenum
rmNodeGetLineStyle(const RMnode * p,RMenum * rval)2658 rmNodeGetLineStyle(const RMnode *p,
2659 RMenum *rval)
2660 {
2661 if (RM_ASSERT(p,"rmNodeGetLineStyle() error: input RMnode is NULL.") == RM_WHACKED)
2662 return(RM_WHACKED);
2663
2664 if (p->rprops == NULL || p->rprops->linestyle == NULL)
2665 return(RM_WHACKED);
2666
2667 if (rval != NULL)
2668 *rval = *(p->rprops->linestyle);
2669
2670 return(RM_CHILL);
2671 }
2672
2673 /*
2674 * ----------------------------------------------------
2675 * @Name rmNodeSetLineWidth
2676 @pstart
2677 RMenum rmNodeSetLineWidth(RMnode *toModify,
2678 RMenum widthEnum)
2679 @pend
2680
2681 @astart
2682 RMnode *toModify - a handle to an RMnode (modified).
2683 RMenum widthEnum - an RMenum value. May be one of RM_LINEWIDTH_NARROW,
2684 RM_LINEWIDTH_MEDIUM, RM_LINEWIDTH_HEAVY, or RM_LINEWIDTH_X, where
2685 1 <= X <= 8 (input).
2686 @aend
2687
2688 @dstart
2689 Use this routine to change the thickness of lines. Be default, all lines
2690 are drawn so they are 1 pixel wide (set on rmRootNode() at the time
2691 when RM is initialized). The following table shows the relationship between
2692 the linewidth enumerators and the pixel thickness of lines. The column on
2693 the left is the actual pixel thickness on the screen, and the second
2694 column are the associated RM line width enumerators.
2695
2696 <pre>
2697 1 - RM_LINEWIDTH_NARROW,RM_LINEWIDTH_1
2698 2 - RM_LINEWIDTH_MEDIUM, RM_LINEWIDTH_2
2699 3 - RM_LINEWIDTH_3
2700 4 - RM_LINEWIDTH_HEAVY, RM_LINEWIDTH_4
2701 5 - RM_LINEWIDTH_5
2702 6 - RM_LINEWIDTH_6
2703 7 - RM_LINEWIDTH_7
2704 8 - RM_LINEWIDTH_8
2705 </pre>
2706
2707 Upon success, RM_CHILL is returned, and the RMnode's linewidth attribute is
2708 set to the value "widthEnum." Upon failure, RM_WHACKED is returned and
2709 the RMnode's line width attribute remains unmodified.
2710
2711 Use rmNodeGetLinewidth to obtain the line width attribute from an RMnode,
2712 or rmStateGetLinewidth to obtain the line width attribute at
2713 render time.
2714 @dend
2715 * ----------------------------------------------------
2716 */
2717 RMenum
rmNodeSetLineWidth(RMnode * n,RMenum width_enum)2718 rmNodeSetLineWidth(RMnode *n,
2719 RMenum width_enum)
2720 {
2721 RMenum t;
2722
2723 if (RM_ASSERT(n,"rmNodeSetLineWidth() error: input RMnode is NULL.") == RM_WHACKED)
2724 return(RM_WHACKED);
2725
2726 t = width_enum;
2727 if ((t != RM_LINEWIDTH_NARROW) && (t != RM_LINEWIDTH_MEDIUM) &&
2728 (t != RM_LINEWIDTH_HEAVY) && (t != RM_LINEWIDTH_1) &&
2729 (t != RM_LINEWIDTH_2) && (t != RM_LINEWIDTH_3) &&
2730 (t != RM_LINEWIDTH_4) && (t != RM_LINEWIDTH_5) &&
2731 (t != RM_LINEWIDTH_6) && (t != RM_LINEWIDTH_7) &&
2732 (t != RM_LINEWIDTH_8))
2733 {
2734 rmError("rmNodeSetLineWidth() error: the input line width enumerator is not valid.");
2735 return(RM_WHACKED);
2736 }
2737
2738 if (n->rprops == NULL)
2739 n->rprops = private_rmRenderModePropsNew();
2740
2741 if (n->rprops->linewidth == NULL)
2742 n->rprops->linewidth = private_rmEnumNew(1);
2743
2744 *(n->rprops->linewidth) = width_enum;
2745 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2746
2747 return(RM_CHILL);
2748 }
2749
2750 /*
2751 * ----------------------------------------------------
2752 * @Name rmNodeGetLineWidth
2753 @pstart
2754 RMenum rmNodeGetLineWidth(const RMnode *toQuery,
2755 RMenum *lineWidthReturn)
2756 @pend
2757
2758 @astart
2759 const RMnode *toQuery - a handle to an RMnode (input).
2760 RMenum *lineWidthReturn - a handle to a caller supplied RMenum (result).
2761 @aend
2762
2763 @dstart
2764 Use this routine to query the line width attribute of an RMnode. If one is
2765 defined, RM_CHILL is returned on the stack, and the RMnode's line width
2766 attribute will be copied into caller-supplied memory. Otherwise, RM_WHACKED
2767 is returned. Please refer to rmNodeSetLineWidth() for details about
2768 line width enumerators.
2769
2770 Note that a linewidth enumerator is returned, not the actual pixel thickness
2771 of the line.
2772
2773 To obtain the render-time value of the line width parameter of the
2774 RM rendering context, use rmStateGetLineWidth() from inside an application
2775 callback.
2776 @dend
2777 * ----------------------------------------------------
2778 */
2779 RMenum
rmNodeGetLineWidth(const RMnode * p,RMenum * rval)2780 rmNodeGetLineWidth(const RMnode *p,
2781 RMenum *rval)
2782 {
2783 /* note that we're returning an enumeration
2784 value, not a line width. it is up to the
2785 caller to correctly convert. */
2786
2787 if (RM_ASSERT(p,"rmNodeGetLineWidth() error: input RMnode is NULL.") == RM_WHACKED)
2788 return(RM_WHACKED);
2789
2790 if (p->rprops == NULL || p->rprops->linewidth == NULL)
2791 return(RM_WHACKED);
2792
2793 if (rval != NULL)
2794 *rval = *(p->rprops->linewidth);
2795
2796 return(RM_CHILL);
2797 }
2798
2799
2800 /*
2801 * ----------------------------------------------------
2802 * @Name rmNodeSetPointSize
2803 @pstart
2804 RMenum rmNodeSetPointSize(RMnode *toModify,
2805 float newSize)
2806 @pend
2807
2808 @astart
2809 RMnode *toModify - a handle to an RMnode (modified).
2810 float newSize - a floating point value, must be greater than zero (input).
2811 @aend
2812
2813 @dstart
2814 By default, a point primitive is drawn as a single pixel on the screen.
2815 This routine is used to change the the number of pixels used when drawing
2816 a point primitive. For non antialiased points (3/2000, currently the only
2817 option), the point primitive is rendered as a square consisting of
2818 "newSize" pixels on each edge of the square. Non-integer values are
2819 rounded to the nearest integer.
2820
2821 Upon success, RM_CHILL is returned, and the new point size value is copied
2822 into the RMnode. Otherwise, RM_WHACKED is returned, and the RMnode remains
2823 unmodified.
2824
2825 Use rmNodeGetPointSize() to obtain the point size attribute from an
2826 RMnode, or rmStateGetPointSize() from within an application callback to
2827 query the current point size from the RM rendering context.
2828 @dend
2829 * ----------------------------------------------------
2830 */
2831 RMenum
rmNodeSetPointSize(RMnode * n,float newsize)2832 rmNodeSetPointSize(RMnode *n,
2833 float newsize)
2834 {
2835 if (RM_ASSERT(n,"rmNodeSetPointSize() error: the input RMnode is NULL") == RM_WHACKED)
2836 return(RM_WHACKED);
2837
2838 if (newsize <= 0.0F)
2839 {
2840 rmError("rmNodeSetPointSize() error: the point size must be greater than zero.");
2841 return(RM_WHACKED);
2842 }
2843
2844 if (n->rprops == NULL)
2845 n->rprops = private_rmRenderModePropsNew();
2846
2847 if (n->rprops->pointsize == NULL)
2848 n->rprops->pointsize = (float *)malloc(sizeof(float));
2849
2850 *(n->rprops->pointsize) = newsize;
2851 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2852 return(RM_CHILL);
2853 }
2854
2855 /*
2856 * ----------------------------------------------------
2857 * @Name rmNodeGetPointSize
2858 @pstart
2859 RMenum rmNodeGetPointSize(const RMnode *toQuery,
2860 float *sizeReturn)
2861 @pend
2862
2863 @astart
2864 const RMnode *toQuery - a handle to an RMnode (input).
2865 float *sizeReturn - a handle to a caller-supplied float (result).
2866 @aend
2867
2868 @dstart
2869 Use this routine to obtain the "point size" attribute from an RMnode. If
2870 it exists, RM_CHILL is returned, and the point size attribute is copied
2871 into caller-supplied memory (if it is not NULL). Otherwise, RM_WHACKED
2872 is returned.
2873
2874 Note this routine queries an RMnode. To obtain the current point size
2875 attribute of the RM rendering context from within an application callback,
2876 use rmStateGetPointSize().
2877 @dend
2878 * ----------------------------------------------------
2879 */
2880 RMenum
rmNodeGetPointSize(const RMnode * p,float * rval)2881 rmNodeGetPointSize(const RMnode *p,
2882 float *rval)
2883 {
2884 if (RM_ASSERT(p,"rmNodeGetPointSize() error: the input RMnode is NULL") == RM_WHACKED)
2885 return(RM_WHACKED);
2886
2887 if (p->rprops == NULL || p->rprops->pointsize == NULL)
2888 return(RM_WHACKED);
2889
2890 if (rval != NULL)
2891 *rval = *(p->rprops->pointsize);
2892 return(RM_CHILL);
2893 }
2894
2895 /*
2896 * ----------------------------------------------------
2897 * @Name rmNodeSetPolygonDrawMode
2898 @pstart
2899 RMenum rmNodeSetPolygonDrawMode(RMnode *toModify,
2900 RMenum whichFace,
2901 RMenum newMode)
2902 @pend
2903
2904 @astart
2905 RMnode *toModify - a handle to an RMnode (modified).
2906 RMenum whichFace - an RMenum value, may be one of RM_FRONT, RM_BACK,
2907 RM_FRONT_AND_BACK (input).
2908 RMenum newMode - an RMenum value, may be one of RM_LINE, RM_FILL or RM_POINT.
2909 @aend
2910
2911 @dstart
2912 This routine maps directly to glPolygonMode() - a polygon has two sides,
2913 a front and back, and might be rendered differently depending upon which
2914 side is facing the viewer. By default, both front and back are drawn
2915 the same way: as filled polygons. This routine allows you to change
2916 how polygons are drawn. There are three options for the parameter "newMode"
2917 which specifies a new polygon rendering mode. RM_FILL, the default
2918 polygon mode, specifies that polygons will be drawn as filled areas.
2919 RM_LINE specifies that only polygon edges will be drawn. RM_POINT
2920 will cause only polygon vertices to be rendered.
2921
2922 For the "whichFace" parameter, RM_FRONT means that front-facing polygons
2923 will be drawn using the new mode. RM_BACK means that back-facing polygons
2924 will be drawn using the new mode. Use RM_FRONT_AND_BACK to apply the
2925 new mode to both front- and back-facing polygons.
2926
2927 Note the OpenRM limitation that it is not possible at this time (3/2000) to
2928 specify one mode for the front-facing polygons and a different mode for
2929 back-facing polygons.
2930
2931 Whether or not a polygon is front- or back-facing is determined by the
2932 "winding rule." See rmNodeSetFrontFace() for more information. Polygons may
2933 be culled based upon whether or not they are front- or back-facing. See
2934 rmNodeSetPolygonCullMode() for more information.
2935
2936 Returns RM_CHILL upon success. Upon failure, the polygon draw mode
2937 remains unmodified, and RM_WHACKED is returned.
2938 @dend
2939 * ----------------------------------------------------
2940 */
2941 RMenum
rmNodeSetPolygonDrawMode(RMnode * n,RMenum face,RMenum mode)2942 rmNodeSetPolygonDrawMode(RMnode *n,
2943 RMenum face,
2944 RMenum mode)
2945 {
2946
2947 if (RM_ASSERT(n,"rmNodeSetPolygonDrawMode() error: the input RMnode pointer is NULL.") == RM_WHACKED)
2948 return(RM_WHACKED);
2949
2950 if ((face != RM_FRONT) && (face != RM_FRONT_AND_BACK) && (face != RM_BACK))
2951 {
2952 rmError(" the face tag given to rmNodeSetPolygonDrawMode is invalid. ");
2953 return(RM_WHACKED);
2954 }
2955
2956 if ((mode != RM_POINT) && (mode != RM_LINE) && (mode != RM_FILL))
2957 {
2958 rmError(" the mode tag given to rmNodeSetPolygonDrawMode is invalid. ");
2959 return(RM_WHACKED);
2960 }
2961
2962 if (n->rprops == NULL)
2963 n->rprops = private_rmRenderModePropsNew();
2964
2965 if (n->rprops->poly_mode_face == NULL)
2966 n->rprops->poly_mode_face = private_rmEnumNew(1);
2967
2968 if (n->rprops->poly_mode_drawstyle == NULL)
2969 n->rprops->poly_mode_drawstyle = private_rmEnumNew(1);
2970
2971 *(n->rprops->poly_mode_face) = face; /* range check? */
2972 *(n->rprops->poly_mode_drawstyle) = mode;
2973
2974 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
2975 return(RM_WHACKED);
2976 }
2977
2978 /*
2979 * ----------------------------------------------------
2980 * @Name rmNodeGetPolygonDrawMode
2981 @pstart
2982 RMenum rmNodeGetPolygonDrawMode(const RMnode *toQuery,
2983 RMenum *returnFace,
2984 RMenum *returnMode)
2985 @pend
2986
2987 @astart
2988 const RMnode *toQuery - a handle to an RMnode (input).
2989 RMenum *returnFace, *returnMode - handles to caller-supplied RMenum's
2990 (result).
2991 @aend
2992
2993 @dstart
2994 Use this routine to obtain the polygon rendering mode specified at an
2995 RMnode. Upon success, RM_CHILL is returned on the stack, and the RMnode's
2996 polygon rendering mode and face are copied into caller-supplied memory.
2997 If the polygon rendering mode is not defined at the RMnode "toQuery,"
2998 RM_WHACKED is returned and caller-supplied memory remains unmodified.
2999 See rmNodeSetPolygonDrawMode() for more details about the return values
3000 for "returnFace" and "returnMode."
3001
3002 Note this routine queries the polygon mode at a specific node. During
3003 rendering, a given RMnode may not have a polygon mode defined at the
3004 RMnode level, but the polygon mode, and other drawing attributes, are
3005 always defined in the RMstate object. Use the routine rmStateGetPolygonDrawMode()
3006 from within an application callback to obtain the current render-time
3007 values for the polygon draw mode.
3008 @dend
3009 * ----------------------------------------------------
3010 */
3011 RMenum
rmNodeGetPolygonDrawMode(const RMnode * n,RMenum * face,RMenum * mode)3012 rmNodeGetPolygonDrawMode(const RMnode *n,
3013 RMenum *face,
3014 RMenum *mode)
3015 {
3016 if (RM_ASSERT(n,"rmNodeGetPolygonDrawMode() error: the input RMnode is NULL") == RM_WHACKED)
3017 return(RM_WHACKED);
3018
3019 if (n->rprops == NULL || n->rprops->poly_mode_face == NULL ||
3020 n->rprops->poly_mode_drawstyle == NULL)
3021 return(RM_WHACKED);
3022
3023 if (face != NULL)
3024 *face = *(n->rprops->poly_mode_face);
3025
3026 if (mode != NULL)
3027 *mode = *(n->rprops->poly_mode_drawstyle);
3028 return(RM_CHILL);
3029 }
3030 /* ----------------------------------------------------
3031 * @Name rmNodeSetPolygonCullMode
3032 @pstart
3033 RMenum rmNodeSetPolygonCullMode(RMnode *toModify,
3034 RMenum newMode)
3035 @pend
3036
3037 @astart
3038 RMnode *toModify - a handle to an RMnode (modified).
3039 RMenum newMode - an RMenum value. May be one of RM_CULL_NONE, RM_CULL_FRONT,
3040 RM_CULL_BACK, or RM_CULL_FRONT_AND_BACK.
3041 @aend
3042
3043 @dstart
3044 Culling refers to the process of rejecting polygons from the
3045 rendering process based upon some criteria. rmNodeSetPolygonCullMode()
3046 is used to control whether or not polygons are culled based upon
3047 whether or not they are front- or back-facing. This type of culling is
3048 performed late in the rendering pipeline - polygon vertices are first
3049 transformed to eye coordinates, then the polygon cull test is applied to
3050 accept or reject the polygon. Higher level culling, such as view frustum
3051 culling, occurs much earlier in the pipeline. Face level culling can
3052 improve performance in many cases, but frustum culling prior to
3053 drawing will result in better performance.
3054
3055 By default, no polygon
3056 culling is performed (RM_CULL_NONE is applied to rmRootNode() at
3057 initialization time).
3058
3059 Whether or not a polygon is front or back facing is a function of how
3060 vertices are ordered. See rmNodeSetFrontFace() for more information.
3061
3062 When the input value for "newMode" is RM_CULL_NONE, no polygon culling
3063 is performed. When set to RM_CULL_FRONT, front-facing polygons will
3064 be culled after transformation to eye coordinates, but prior to drawing.
3065 When RM_CULL_BACK is specified, only back-facing polygons are culled
3066 prior to rasterization. When RM_CULL_FRONT_AND_BACK is specified, all
3067 polygons are culled.
3068 @dend
3069 * ----------------------------------------------------
3070 */
3071 RMenum
rmNodeSetPolygonCullMode(RMnode * n,RMenum newmode)3072 rmNodeSetPolygonCullMode(RMnode *n,
3073 RMenum newmode)
3074 {
3075 if (RM_ASSERT(n,"rmNodeSetPolygonCullMode() error: input node is NULL.") == RM_WHACKED)
3076 return(RM_WHACKED);
3077
3078 if ((newmode != RM_CULL_NONE) && (newmode != RM_CULL_FRONT) &&
3079 (newmode != RM_CULL_BACK) && (newmode != RM_CULL_FRONT_AND_BACK))
3080 {
3081 rmError(" the cull mode given to rmNodeSetPolygonCullMode is invalid. \n");
3082 return(RM_WHACKED);
3083 }
3084 if (n->rprops == NULL)
3085 n->rprops = private_rmRenderModePropsNew();
3086
3087 if (n->rprops->cull_mode == NULL)
3088 n->rprops->cull_mode = private_rmEnumNew(1);
3089
3090 *(n->rprops->cull_mode) = newmode;
3091 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
3092
3093 return(RM_CHILL);
3094 }
3095
3096 /*
3097 * ----------------------------------------------------
3098 * @Name rmNodeGetPolygonCullMode
3099 @pstart
3100 RMenum rmNodeGetPolygonCullMode(const RMnode *toQuery,
3101 RMenum *modeReturn)
3102 @pend
3103
3104 @astart
3105 const RMnode *toQuery - a handle to an RMnode (input).
3106 RMenum *modeReturn - a handle to a caller-supplied RMenum (result).
3107 @aend
3108
3109 @dstart
3110 Use this routine to obtain polygon cull settings at an RMnode. Upon success,
3111 RM_CHILL is returned, and the polygon cull mode from the RMnode "toQuery"
3112 is copied into caller-supplied memory. Upon failure, RM_WHACKED is returned,
3113 and caller-supplied memory remains unmodified.
3114
3115 Note that this routine obtains the polygon cull settings from an RMnode.
3116 Not all RMnode's have this attribute defined. During a render-time traversal
3117 of the scene graph, the current rendering context may be queried using
3118 rmStateGetPolygonCullMode() from an application callback.
3119 @dend
3120 * ----------------------------------------------------
3121 */
3122 RMenum
rmNodeGetPolygonCullMode(const RMnode * n,RMenum * retmode)3123 rmNodeGetPolygonCullMode(const RMnode *n,
3124 RMenum *retmode)
3125 {
3126 if (RM_ASSERT(n,"rmNodeGetPolygonCullMode() error: input RMnode is NULL. \n") == RM_WHACKED)
3127 return(RM_WHACKED);
3128
3129 if (n->rprops == NULL || n->rprops->cull_mode == NULL)
3130 return(RM_WHACKED);
3131
3132 if (retmode != NULL)
3133 *retmode = *(n->rprops->cull_mode);
3134 return(RM_CHILL);
3135 }
3136
3137 /*
3138 * ----------------------------------------------------
3139 * @Name rmNodeSetFrontFace
3140 @pstart
3141 RMenum rmNodeSetFrontFace(RMnode *toModify,
3142 RMenum newMode)
3143 @pend
3144
3145 @astart
3146 RMnode *toModify - a handle to an RMnode (modified).
3147 RMenum newMode - an RMenum value, may be one of RM_CW or RM_CCW (input).
3148 @aend
3149
3150 @dstart
3151 This routine is used to control how OpenGL determines which faces are
3152 front--facing. The basic idea is that the ordering of vertices
3153 in projected screen coordinates determines if a polygon is front-facing.
3154 Two choices are possible: either a clockwise orientation,
3155 specified with RM_CW, or a counter-clockwise orientation, RM_CCW.
3156
3157 Whether or not a polygon is front- or back-facing as computed by
3158 this winding rule is relevant only in the context of face-level culling.
3159 Lighting calculations, in contrast, are a function of the dot product
3160 of the surface normal with the direction to the light source.
3161
3162 By default, front faces in OpenRM are specified with RM_CCW, so
3163 polygon vertices that have a counterclockwise winding are front-facing.
3164 This is accomplished by setting rmNodeSetFrontFace(rmRootNode(), RM_CCW)
3165 at the time RM is initialized (this is performed internal to RM - applications
3166 need not perform this task).
3167
3168 Use rmNodeGetFrontFace() to obtain the front face winding rule parameter
3169 from an RMnode.
3170 @dend
3171 * ----------------------------------------------------
3172 */
3173 RMenum
rmNodeSetFrontFace(RMnode * n,RMenum newmode)3174 rmNodeSetFrontFace(RMnode *n,
3175 RMenum newmode)
3176 {
3177 if (RM_ASSERT(n,"rmNodeSetFrontFace() error: input RMnode is NULL. \n") == RM_WHACKED)
3178 return(RM_WHACKED);
3179
3180 if ((newmode != RM_CW) && (newmode != RM_CCW))
3181 {
3182 rmError(" the mode given to rmNodeSetFrontface is invalid. ");
3183 return(RM_WHACKED);
3184 }
3185
3186 if (n->rprops == NULL)
3187 n->rprops = private_rmRenderModePropsNew();
3188
3189 if (n->rprops->front_face == NULL)
3190 n->rprops->front_face = private_rmEnumNew(1);
3191
3192 *(n->rprops->front_face) = newmode;
3193
3194 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
3195 return(RM_CHILL);
3196 }
3197
3198 /*
3199 * ----------------------------------------------------
3200 * @Name rmNodeGetFrontFace
3201 @pstart
3202 RMenum rmNodeGetFrontFace(const RMnode *toQuery,
3203 RMenum *modeReturn)
3204 @pend
3205
3206 @astart
3207 const RMnode *toQuery - a handle to an RMnode (input).
3208 RMenum *modeReturn - a handle to a caller-supplied RMenum (result).
3209 @aend
3210
3211 @dstart
3212 Use this routine to obtain the "front face" parameter from an RMnode.
3213 Upon success, RM_CHILL is returned, and the front face attribute from
3214 the RMnode "toQuery" is copied into caller-supplied memory. Upon failure,
3215 RM_WHACKED is returned, and caller-supplied memory remains unmodified.
3216
3217 Note that this routine obtains the front face attribute from an RMnode.
3218 Use the routine rmStateGetFrontFace() to obtain the front face attribute
3219 from the render state from inside an application callback during a
3220 render-time traversal of the scene graph.
3221 @dend
3222 * ----------------------------------------------------
3223 */
3224 RMenum
rmNodeGetFrontFace(const RMnode * n,RMenum * rval)3225 rmNodeGetFrontFace(const RMnode *n,
3226 RMenum *rval)
3227 {
3228 if (RM_ASSERT(n,"rmNodeGetFrontFace() error: input RMnode is NULL. \n") == RM_WHACKED)
3229 return(RM_WHACKED);
3230
3231 if (n->rprops == NULL || n->rprops->front_face == NULL)
3232 return(RM_WHACKED);
3233
3234 if (rval != NULL)
3235 *rval = *(n->rprops->front_face);
3236 return(RM_CHILL);
3237 }
3238
3239 /*
3240 * ----------------------------------------------------
3241 * @Name rmNodeSetShader
3242 @pstart
3243 RMenum rmNodeSetShader(RMnode *toModify,
3244 RMenum newMode)
3245 @pend
3246
3247 @astart
3248 RMnode *toModify - a handle to an RMnode (modified).
3249 RMenum newMode - an RMenum value (input). Must be one of RM_SHADER_SMOOTH,
3250 RM_SHADER_FLAT or RM_SHADER_NOLIGHT.
3251 @aend
3252
3253 @dstart
3254 Use this routine to set the shading model used for rendering primitives.
3255 The new shader, specified by newMode, takes effect at the RMnode "toModify"
3256 and has scope of control over all descendent nodes in the scene graph,
3257 unless overridden by a new shader value specified at some child node.
3258 The permissible values for "newMode" are RM_SHADER_SMOOTH, RM_SHADER_FLAT
3259 or RM_SHADER_NOLIGHT. RM_SHADER_SMOOTH corresponds to
3260 glShadeModel(GL_SMOOTH); RM_SHADER_FLAT corresponds to glShadeModel(GL_FLAT)
3261 and RM_SHADER_NOLIGHT is a combination of glShadeModel(GL_FLAT) with
3262 lighting disabled.
3263
3264 RM_SHADER_SMOOTH causes OpenGL's "smooth shading" model to be used.
3265 In most implementations, this is a Gouroud shader. RM_SHADER_FLAT uses
3266 a single color for each polygon or line segment. Some implementations
3267 will average all vertex colors, within the polygon or line segment, to
3268 produce an average color for the entire polygon or line segment. Other
3269 implementations use, instead, the last color value specified.
3270 RM_SHADER_NOLIGHT is the same as RM_SHADER_FLAT, but with lighting
3271 disabled.
3272
3273 Upon success, the shade model of the input RMnode is modified to contain
3274 the value specified by "newMode," and RM_CHILL is returned. Upon failure,
3275 caused by a NULL input RMnode or an out-of-range shade model enumerator,
3276 RM_WHACKED is returned and the RMnode "toModify" remains unmodified.
3277
3278 By default, rmRootNode() is assigned RM_SHADER_SMOOTH as the default
3279 shader, and will remain in effect unless overridden.
3280 @dend
3281 * ----------------------------------------------------
3282 */
3283 RMenum
rmNodeSetShader(RMnode * n,RMenum newMode)3284 rmNodeSetShader(RMnode *n,
3285 RMenum newMode)
3286 {
3287 if (RM_ASSERT(n,"rmNodeSetShader() error: the input RMnode is NULL") == RM_WHACKED)
3288 return(RM_WHACKED);
3289
3290 if ((newMode != RM_SHADER_SMOOTH) && (newMode != RM_SHADER_FLAT) &&
3291 (newMode != RM_SHADER_NOLIGHT))
3292 {
3293 rmError("rmNodeSetShader() error: the input RMenum shader specification is not one of RM_SHADER_SMOOTH, RM_SHADER_FLAT or RM_SHADER_NOLIGHT");
3294 return(RM_WHACKED);
3295 }
3296
3297 if (n->rprops == NULL)
3298 n->rprops = private_rmRenderModePropsNew();
3299
3300 if (n->rprops->shademodel == NULL)
3301 n->rprops->shademodel = private_rmEnumNew(1);
3302
3303 *(n->rprops->shademodel) = newMode;
3304
3305 private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
3306
3307 return(RM_WHACKED);
3308 }
3309
3310 /*
3311 * ----------------------------------------------------
3312 * @Name rmNodeGetShader
3313 @pstart
3314 RMenum rmNodeGetShader(const RMnode *toQuery,
3315 RMenum *shaderReturn)
3316 @pend
3317
3318 @astart
3319 const RMnode *n - a handle to an RMnode (input).
3320 RMenum *shaderReturn - a handle to an RMenum (result).
3321 @aend
3322
3323 @dstart
3324 Use this routine to obtain the shader parameter associated with an
3325 RMnode. If no shader parameter is present in the RMnode "toQuery,"
3326 or if "toQuery" or "shaderReturn" are NULL, RM_WHACKED is returned
3327 and "shaderReturn" remains unmodified. Otherwise, the shader parameter
3328 of "toQuery" is copied into caller-supplied memory and RM_CHILL is
3329 returned to the caller.
3330
3331 Note that this routine does not return the shader that would be active
3332 at "toQuery" during a render-time traversal of the scene graph. If an
3333 RMnode does not have a shading parameter, the shader is inherited from
3334 ancestor nodes. The only means at this time (March 2000) to obtain the
3335 shader that would be active during a render-time traversal of the
3336 scene graph is to attach a switch or post-traversal callback to an
3337 RMnode, then query the RMstate using rmStateGetShader()) (a pre-traversal
3338 callback won't work because all rendering parameters are processed
3339 after the pre-traversal callback).
3340 @dend
3341 * ----------------------------------------------------
3342 */
3343 RMenum
rmNodeGetShader(const RMnode * n,RMenum * shaderReturn)3344 rmNodeGetShader(const RMnode *n,
3345 RMenum *shaderReturn)
3346 {
3347 if ((RM_ASSERT(n,"rmNodeGetShader() error: the input RMnode is NULL") == RM_WHACKED) ||
3348 (RM_ASSERT(shaderReturn,"rmNodeGetShader() error: the return RMenum parameter is NULL") == RM_WHACKED))
3349 return(RM_WHACKED);
3350
3351 if (n->rprops == NULL || n->rprops->shademodel == NULL)
3352 return(RM_WHACKED);
3353 *shaderReturn = *(n->rprops->shademodel);
3354 return(RM_CHILL);
3355 }
3356
3357
3358 /*
3359 * ----------------------------------------------------
3360 * @Name rmNodeSetTraversalMaskOpacity
3361 @pstart
3362 RMenum rmNodeSetTraversalMaskOpacity(RMnode *toModify,
3363 RMenum newVal)
3364 @pend
3365
3366 @astart
3367 RMnode *toModify - a handle to an RMnode (modified).
3368 RMenum newVal - an RMenum value. May be one of RM_RENDERPASS_TRANSPARENT,
3369 RM_RENDERPASS_OPAQUE or RM_RENDERPASS_ALL.
3370 @aend
3371
3372 @dstart
3373 This routine is used to update one of the traversal masks of the
3374 RMnode "toModify." Upon success, the "opacity" traversal mask is modified
3375 to contain the value "newVal", and RM_CHILL is returned to the caller.
3376 Upon failure, RM_WHACKED is returned and the RMnode's opacity
3377 traversal mask remains unmodified.
3378
3379 The RMnode traversal mask is used to control whether or not a RMnode,
3380 and its descendents in the scene graph, are processed during a
3381 scene graph traversal. Three types of traversal masks are supported:
3382 opacity (rmNodeSetTraversalMaskOpacity), dimensions
3383 (rmNodeSetTraversalMaskDims), and stereo channel
3384 (rmNodeSetTraversalMaskChannel), although more may be added
3385 in the future. Currently, the traversal masks of RMnodes are tested
3386 only during a render-time traversal of the scene graph. The traversal
3387 masks provide a way for nodes to be processed during one or multiple
3388 passes of multipass rendering.
3389
3390 In this routine, the permissible values of "newVal" are RM_RENDERPASS_OPAQUE,
3391 RM_RENDERPASS_TRANSPARENT or RM_RENDERPASS_ALL. Setting the value to
3392 RM_RENDERPASS_OPAQUE will cause nodes, and their descendents, to be
3393 processed only during an "opaque" rendering pass; a traversal mask
3394 value of RM_RENDERPASS_TRANSPARENT will result in nodes and their
3395 descendents being processed only during a "transparent" rendering pass,
3396 and RM_RENDERPASS_ALL will cause nodes and their descendents to be
3397 processed during both opaque and transparent rendering passes.
3398 @dend
3399 * ----------------------------------------------------
3400 */
3401 RMenum
rmNodeSetTraversalMaskOpacity(RMnode * n,RMenum i)3402 rmNodeSetTraversalMaskOpacity(RMnode *n,
3403 RMenum i)
3404 {
3405 if (RM_ASSERT(n,"rmNodeSetTraversalMaskOpacity() error: the input RMnode is NULL") == RM_WHACKED)
3406 return(RM_WHACKED);
3407
3408 if ((i != RM_RENDERPASS_OPAQUE) && (i != RM_RENDERPASS_ALL) &&
3409 (i != RM_RENDERPASS_TRANSPARENT))
3410 {
3411 rmWarning("rmNodeSetTraversalMaskOpacity() error: the input RMenum value is not one of RM_RENDERPASS_OPAQUE, RM_RENDERPASS_TRANSPARENT or RM_RENDERPASS_ALL");
3412 return(RM_WHACKED);
3413 }
3414
3415 private_rmNodeSetTraversalMaskOpacity(n,i);
3416 return(RM_CHILL);
3417 }
3418
3419 /*
3420 * ----------------------------------------------------
3421 * @Name rmNodeGetTraversalMaskOpacity
3422 @pstart
3423 RMenum rmNodeGetTraversalMaskOpacity(const RMnode *toQuery,
3424 RMenum *maskReturn)
3425 @pend
3426
3427 @astart
3428 const RMnode *toQuery - a handle to an RMnode (input).
3429 RMenum *maskReturn - a handle to an RMenum value (result).
3430 @aend
3431
3432 @dstart
3433 Use this routine to obtain the opacity traversal mask from an
3434 RMnode. Upon success, the opacity traversal mask from the RMnode
3435 "toQuery" is copied into caller-supplied memory, and RM_CHILL
3436 is returned. Otherwise, RM_WHACKED is returned, and the caller-supplied
3437 memory is not modified.
3438
3439 See rmNodeSetTraversalMaskOpacity for more information about the
3440 opacity traversal mask.
3441 @dend
3442 * ----------------------------------------------------
3443 */
3444
3445 RMenum
rmNodeGetTraversalMaskOpacity(const RMnode * n,RMenum * maskRet)3446 rmNodeGetTraversalMaskOpacity(const RMnode *n,
3447 RMenum *maskRet)
3448 {
3449 if ((RM_ASSERT(n,"rmNodeGetTraversalMaskOpacity() error: the input RMnode is NULL") == RM_WHACKED) ||
3450 (RM_ASSERT(maskRet,"rmNodeGetTraversalMaskOpacity() error: the return RMenum parameter is NULL") == RM_WHACKED))
3451 return(RM_WHACKED);
3452
3453 *maskRet = private_rmNodeGetTraversalMaskOpacity(n);
3454 return(RM_CHILL);
3455 }
3456
3457 /*
3458 * ----------------------------------------------------
3459 * @Name rmNodeSetTraversalMaskDims
3460 @pstart
3461 RMenum rmNodeSetTraversalMaskDims(RMnode *toModify,
3462 RMenum newVal)
3463 @pend
3464
3465 @astart
3466 RMnode *toModify - a handle to an RMnode (modified).
3467 RMenum newVal - an RMenum value. May be one of RM_RENDERPASS_2D,
3468 RM_RENDERPASS_3D or RM_RENDERPASS_ALL.
3469 @aend
3470
3471 @dstart
3472 This routine is used to update one of the traversal masks of the
3473 RMnode "toModify." Upon success, the "dimensions" traversal mask is modified
3474 to contain the value "newVal", and RM_CHILL is returned to the caller.
3475 Upon failure, RM_WHACKED is returned and the RMnode's opacity
3476 traversal mask remains unmodified.
3477
3478 The RMnode traversal mask is used to control whether or not a RMnode,
3479 and its descendents in the scene graph, are processed during a
3480 scene graph traversal. Three types of traversal masks are supported:
3481 opacity (rmNodeSetTraversalMaskOpacity), dimensions
3482 (rmNodeSetTraversalMaskDims), and stereo channel
3483 (rmNodeSetTraversalMaskChannel), although more may be added
3484 in the future. Currently, the traversal masks of RMnodes are tested
3485 only during a render-time traversal of the scene graph. The traversal
3486 masks provide a way for nodes to be processed during one or multiple
3487 passes of multipass rendering.
3488
3489 In this routine, the permissible values of "newVal" are RM_RENDERPASS_2D,
3490 RM_RENDERPASS_3D or RM_RENDERPASS_ALL. Setting the value to
3491 RM_RENDERPASS_3D will cause nodes, and their descendents, to be
3492 processed only during "3D" rendering passes; a traversal mask
3493 value of RM_RENDERPASS_2D will result in nodes and their
3494 descendents being processed only during a "2D" rendering pass,
3495 and RM_RENDERPASS_ALL will cause nodes and their descendents to be
3496 processed during both 2D and 3D rendering passes.
3497 @dend
3498 * ----------------------------------------------------
3499 */
3500 RMenum
rmNodeSetTraversalMaskDims(RMnode * n,RMenum i)3501 rmNodeSetTraversalMaskDims(RMnode *n,
3502 RMenum i)
3503 {
3504 if (RM_ASSERT(n,"rmNodeSetTraversalMaskDims() error: the input RMnode is NULL") == RM_WHACKED)
3505 return(RM_WHACKED);
3506
3507 if ((i != RM_RENDERPASS_3D) && (i != RM_RENDERPASS_ALL) &&
3508 (i != RM_RENDERPASS_2D))
3509 {
3510 rmWarning("rmNodeSetTraversalMaskDims() error: the input RMenum value is not one of RM_RENDERPASS_3D, RM_RENDERPASS_2D or RM_RENDERPASS_ALL");
3511 return(RM_WHACKED);
3512 }
3513
3514 private_rmNodeSetTraversalMaskDims(n,i);
3515 return(RM_CHILL);
3516 }
3517
3518 /*
3519 * ----------------------------------------------------
3520 * @Name rmNodeGetTraversalMaskDims
3521 @pstart
3522 RMenum rmNodeGetTraversalMaskDims(const RMnode *toQuery,
3523 RMenum *maskReturn)
3524 @pend
3525
3526 @astart
3527 const RMnode *toQuery - a handle to an RMnode (input).
3528 RMenum *maskReturn - a handle to an RMenum value (result).
3529 @aend
3530
3531 @dstart
3532 Use this routine to obtain the "dimensions" traversal mask from an
3533 RMnode. Upon success, the dimensions traversal mask from the RMnode
3534 "toQuery" is copied into caller-supplied memory, and RM_CHILL
3535 is returned. Otherwise, RM_WHACKED is returned, and the caller-supplied
3536 memory is not modified.
3537
3538 See rmNodeSetTraversalMaskDims for more information about the
3539 dimensions traversal mask.
3540 @dend
3541 * ----------------------------------------------------
3542 */
3543 RMenum
rmNodeGetTraversalMaskDims(RMnode * n,RMenum * maskRet)3544 rmNodeGetTraversalMaskDims(RMnode *n,
3545 RMenum *maskRet)
3546 {
3547 if ((RM_ASSERT(n,"rmNodeGetTraversalMaskDims() error: the input RMnode is NULL") == RM_WHACKED) ||
3548 (RM_ASSERT(maskRet,"rmNodeGetTraversalMaskDims() error: the return RMenum parameter is NULL") == RM_WHACKED))
3549 return(RM_WHACKED);
3550
3551 *maskRet = private_rmNodeGetTraversalMaskDims(n);
3552 return(RM_CHILL);
3553 }
3554
3555 /*
3556 * ----------------------------------------------------
3557 * @Name rmNodeSetTraversalMaskChannel
3558 @pstart
3559 RMenum rmNodeSetTraversalMaskChannel(RMnode *toModify,
3560 RMenum newVal)
3561 @pend
3562
3563 @astart
3564 RMnode *toModify - a handle to an RMnode (modified).
3565 RMenum newVal - an RMenum value. May be one of RM_LEFT_CHANNEL,
3566 RM_RIGHT_CHANNEL or RM_ALL_CHANNELS.
3567 @aend
3568
3569 @dstart
3570 This routine is used to update one of the traversal masks of the
3571 RMnode "toModify." Upon success, the "channel" traversal mask is modified
3572 to contain the value "newVal", and RM_CHILL is returned to the caller.
3573 Upon failure, RM_WHACKED is returned and the RMnode's channel
3574 traversal mask remains unmodified.
3575
3576 The RMnode traversal mask is used to control whether or not a RMnode,
3577 and its descendents in the scene graph, are processed during a
3578 scene graph traversal. Three types of traversal masks are supported:
3579 opacity (rmNodeSetTraversalMaskOpacity), dimensions
3580 (rmNodeSetTraversalMaskDims), and stereo channel
3581 (rmNodeSetTraversalMaskChannel), although more may be added
3582 in the future. Currently, the traversal masks of RMnodes are tested
3583 only during a render-time traversal of the scene graph. The traversal
3584 masks provide a way for nodes to be processed during one or multiple
3585 passes of multipass rendering.
3586
3587 In this routine, the permissible values of "newVal" are RM_LEFT_CHANNEL,
3588 RM_RIGHT_CHANNEL or RM_ALL_CHANNELS. Setting the value to
3589 RM_LEFT_CHANNEL will cause nodes, and their descendents, to be
3590 processed only during the left-channel rendering pass of a multipass
3591 stereo rendering; a traversal mask
3592 value of RM_RIGHT_CHANNEL will result in nodes and their
3593 descendents being processed only during the right-channel rendering pass
3594 of a multipass stereo rendering,
3595 and RM_ALL_CHANNELS will cause nodes and their descendents to be
3596 processed during any rendering pass of a multipass stereo or mono channel
3597 rendering.
3598 @dend
3599 * ----------------------------------------------------
3600 */
3601 RMenum
rmNodeSetTraversalMaskChannel(RMnode * n,RMenum newval)3602 rmNodeSetTraversalMaskChannel(RMnode *n,
3603 RMenum newval)
3604 {
3605 if (RM_ASSERT(n,"rmNodeSetTraversalMaskChannel error() the input RMnode is NULL.") == RM_WHACKED)
3606 return(RM_WHACKED);
3607
3608 if ((newval != RM_LEFT_CHANNEL) && (newval != RM_RIGHT_CHANNEL) &&
3609 (newval != RM_ALL_CHANNELS))
3610 {
3611 rmError("rmNodeSetTraversalMaskChannel() error: the input channel enumerator is not one of RM_LEFT_CHANNEL, RM_RIGHT_CHANNEL, or RM_ALL_CHANNELS");
3612 return(RM_WHACKED);
3613 }
3614
3615 private_rmNodeSetTraversalMaskChannel(n,newval);
3616 return(RM_CHILL);
3617 }
3618
3619 /*
3620 * ----------------------------------------------------
3621 * @Name rmNodeGetTraversalMaskChannel
3622 @pstart
3623 RMenum rmNodeGetTraversalMaskChannel(const RMnode *toQuery,
3624 RMenum *maskReturn)
3625 @pend
3626
3627 @astart
3628 const RMnode *toQuery - a handle to an RMnode to query (input).
3629 RMenum *maskReturn - a handle to an RMenum value (result).
3630 @aend
3631
3632 @dstart
3633 Use this routine to obtain the channel traversal mask from an
3634 RMnode. Upon success, the channel traversal mask from the RMnode
3635 "toQuery" is copied into caller-supplied memory, and RM_CHILL
3636 is returned. The return value copied into maskReturn will be one
3637 of RM_LEFT_CHANNEL, RM_RIGHT_CHANNEL or RM_ALL_CHANNELS.
3638 Otherwise, RM_WHACKED is returned, and the caller-supplied
3639 memory is not modified.
3640
3641 See rmNodeSetTraversalMaskChannel for more information about the
3642 opacity traversal mask.
3643 @dend
3644 * ----------------------------------------------------
3645 */
3646 RMenum
rmNodeGetTraversalMaskChannel(const RMnode * n,RMenum * maskReturn)3647 rmNodeGetTraversalMaskChannel(const RMnode *n,
3648 RMenum *maskReturn)
3649 {
3650 if ((RM_ASSERT(n,"rmNodeGetTraversalMaskChannel() error: the input RMnode is NULL ") == RM_WHACKED) ||
3651 (RM_ASSERT(n,"rmNodeGetTraversalMaskChannel() error: the maskReturn parameter is NULL")))
3652 return(RM_WHACKED);
3653
3654 *maskReturn = private_rmNodeGetTraversalMaskChannel(n);
3655 return(RM_CHILL);
3656 }
3657
3658
3659 /* node transformations */
3660
3661 /*
3662 * ----------------------------------------------------
3663 * @Name rmNodeSetTransformMode
3664 @pstart
3665 RMenum rmNodeSetTransformMode (RMnode *toModify,
3666 RMenum newMode)
3667 @pend
3668
3669 @astart
3670 RMnode *toModify - a handle to an RMnode (modified).
3671
3672 RMenum newMode - an RMenum value. Must be one of
3673 RM_TRANSFORM_GEOMETRY, RM_TRANSFORM_TEXTURE, or
3674 RM_TRANSFORM_IGNORE.
3675 @aend
3676
3677 @dstart
3678
3679 Use this routine to set the transformation mode attribute in an
3680 RMnode. This attribute controls how the transformation matrix
3681 information is applied to the scene graph. Returns RM_CHILL upon
3682 success, or RM_WHACKED upon failure.
3683
3684 The mode RM_TRANSFORM_GEOMETRY causes transformation attributes
3685 within an RMnode to be applied to the GL_MODELVIEW matrix stack.
3686 RM_TRANSFORM_TEXTURE results in modification of the GL_TEXTURE matrix
3687 stack. RM_TRANSFORM_IGNORE will disable the application of an
3688 RMnode's transformation attributes without requiring transformation
3689 attributes to be neutralized (zero vectors and Identity matrices).
3690
3691 By default, RMnodes are assigned a transformation mode of
3692 RM_TRANSFORM_GEOMETRY when created by rmNodeNew().
3693
3694 Note that RMnode transformations are ALWAYS applied prior to scene
3695 parameter processing within an RMnode. This means that any lights,
3696 cameras, etc. that are part of an RMnode's scene parameters will be
3697 affected by transformations within the node.
3698
3699 @dend
3700 * ----------------------------------------------------
3701 */
3702 RMenum
rmNodeSetTransformMode(RMnode * n,RMenum newmode)3703 rmNodeSetTransformMode (RMnode *n,
3704 RMenum newmode)
3705 {
3706 if (RM_ASSERT(n, "rmNodeSetTransformMode() error: the input RMnode pointer is NULL. ") == RM_WHACKED)
3707 return(RM_WHACKED);
3708
3709 if ((newmode != RM_TRANSFORM_IGNORE) && (newmode != RM_TRANSFORM_GEOMETRY) && (newmode != RM_TRANSFORM_TEXTURE))
3710 {
3711 rmWarning(" rmNodeSetTransformMode is invalid, existing transform mode is unchanged. \n");
3712 return(RM_WHACKED);
3713 }
3714 if (n->transforms == NULL)
3715 n->transforms = private_rmNodeTransformsNew();
3716
3717 n->transforms->transform_mode = newmode;
3718
3719 return(RM_CHILL);
3720 }
3721
3722
3723 /*
3724 * ----------------------------------------------------
3725 * @Name rmNodeGetTransformMode
3726 @pstart
3727 RMenum rmNodeGetTransformMode (const RMnode *toQuery)
3728 @pend
3729
3730 @astart
3731 const RMnode *toQuery - a handle to the RMnode to query (input).
3732 @aend
3733
3734 @dstart
3735
3736 Upon success, the transform mode of the input RMnode is returned to
3737 the caller, and the return value will be one of
3738 RM_TRANSFORM_GEOMETRY, RM_TRANSFORM_TEXTURE or
3739 RM_TRANSFORM_IGNORE. Upon failure, RM_WHACKED is returned. Failure is
3740 defined as a NULL input RMnode, or if the input RMnode has no
3741 transformation attributes.
3742
3743 The transformation mode of RMnodes is by default
3744 RM_TRANSFORM_GEOMETRY, as set by rmNodeNew(), but may be later
3745 changed by using rmNodeSetTransformMode.
3746
3747 @dend
3748 * ----------------------------------------------------
3749 */
3750 RMenum
rmNodeGetTransformMode(const RMnode * n)3751 rmNodeGetTransformMode (const RMnode *n)
3752 {
3753 if (RM_ASSERT(n, "rmNodeGetTransformMode() error: the input RMnode pointer is NULL") == RM_WHACKED)
3754 return(RM_WHACKED);
3755
3756 if (n->transforms != NULL)
3757 return(n->transforms->transform_mode);
3758 else
3759 return(RM_WHACKED);
3760 }
3761
3762
3763 /*
3764 * ----------------------------------------------------
3765 * @Name rmNodeSetPreMatrix
3766 @pstart
3767 RMenum rmNodeSetPreMatrix (RMnode *toModify,
3768 const RMmatrix *newMatrix)
3769 @pend
3770
3771 @astart
3772 RMnode *toModify - a handle to an RMnode (modified).
3773
3774 const RMmatrix *newMatrix - a handle to a caller-supplied RMmatrix
3775 (input).
3776 @aend
3777
3778 @dstart
3779
3780 Use this routine to set the "pre matrix" transformation attribute of
3781 an RMnode.
3782
3783 See rmNodeGetCompositeModelMatrix() for a discussion of how the
3784 transformation attributes of an RMnode are combined into a single
3785 transformation.
3786
3787 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
3788
3789 @dend
3790 * ----------------------------------------------------
3791 */
3792 RMenum
rmNodeSetPreMatrix(RMnode * n,const RMmatrix * newMatrix)3793 rmNodeSetPreMatrix (RMnode *n,
3794 const RMmatrix *newMatrix)
3795 {
3796 if ((RM_ASSERT(n, "rmNodeSetPreMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
3797 (RM_ASSERT(newMatrix, "rmNodeSetPreMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
3798 return(RM_WHACKED);
3799
3800 if (n->transforms == NULL)
3801 n->transforms = private_rmNodeTransformsNew();
3802
3803 n->transforms->pre = *newMatrix;
3804
3805 return(RM_CHILL);
3806 }
3807
3808
3809 /*
3810 * ----------------------------------------------------
3811 * @Name rmNodeGetPreMatrix
3812 @pstart
3813 RMenum rmNodeGetPreMatrix (const RMnode *toQuery,
3814 RMmatrix *matrixReturn)
3815 @pend
3816
3817 @astart
3818 const RMnode *toQuery - a handle to the RMnode to query (input).
3819
3820 RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
3821 (modified).
3822 @aend
3823
3824 @dstart
3825
3826 If the RMnode toQuery has transformation attributes defined, the "pre
3827 matrix" is copied into caller-supplied memory, and RM_CHILL is
3828 returned to the caller. Otherwise, RM_WHACKED is returned and the
3829 caller-supplied memory remains undisturbed.
3830
3831 @dend
3832 * ----------------------------------------------------
3833 */
3834 RMenum
rmNodeGetPreMatrix(const RMnode * n,RMmatrix * ret)3835 rmNodeGetPreMatrix (const RMnode *n,
3836 RMmatrix *ret)
3837 {
3838 if ((RM_ASSERT(n, "rmNodeGetPreMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
3839 (RM_ASSERT(ret, "rmNodeGetPreMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
3840 return(RM_WHACKED);
3841
3842 if (n->transforms == NULL)
3843 return(RM_WHACKED);
3844
3845 *ret = n->transforms->pre;
3846
3847 return(RM_CHILL);
3848 }
3849
3850
3851 /*
3852 * ----------------------------------------------------
3853 * @Name rmNodeSetCenter
3854 @pstart
3855 RMenum rmNodeSetCenter (RMnode *toModify,
3856 const RMvertex3D *newVertex)
3857 @pend
3858
3859 @astart
3860 RMnode *toModify - a handle to an RMnode to modify (modified).
3861
3862 const RMvertex3D *newVertex - a handle to an RMvertex3D (input).
3863 @aend
3864
3865 @dstart
3866
3867 Use this routine to set the "center point" of an RMnode. The "center
3868 point" attribute defines the a local origin about which rotation and
3869 scaling occur for any transformations at this RMnode. See
3870 rmNodeGetCompositeModelMatrix() for a discussion of how the RMnode
3871 transformation attributes are combined into a single, composite
3872 transformation matrix.
3873
3874 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
3875
3876 @dend
3877 * ----------------------------------------------------
3878 */
3879 RMenum
rmNodeSetCenter(RMnode * n,const RMvertex3D * newVertex)3880 rmNodeSetCenter (RMnode *n,
3881 const RMvertex3D *newVertex)
3882 {
3883 if ((RM_ASSERT(n, "rmNodeSetCenter() error: the input RMnode pointer is NULL. ") == RM_WHACKED) ||
3884 (RM_ASSERT(newVertex, "rmNodeSetCenter() error: the RMvertex3D pointer is NULL.") == RM_WHACKED))
3885 return(RM_WHACKED);
3886
3887 #if 0
3888 /* changed on 1/8/2001 */
3889 if (n->transforms == NULL)
3890 n->transforms = private_rmNodeTransformsNew();
3891
3892 n->transforms->center = *newVertex;
3893 #endif
3894 n->center = *newVertex;
3895
3896 return(RM_CHILL);
3897 }
3898
3899
3900 /*
3901 * ----------------------------------------------------
3902 * @Name rmNodeGetCenter
3903 @pstart
3904 RMenum rmNodeGetCenter (const RMnode *toQuery,
3905 RMvertex3D *retVector)
3906 @pend
3907
3908 @astart
3909 const RMnode *toQuery - a handle to an RMnode to query (input).
3910
3911 RMvertex3D *retVector - a handle to a caller-supplied RMvertex3D
3912 object (modified).
3913 @aend
3914
3915 @dstart
3916
3917 Use this routine to obtain the RMnode's "center point." If the input
3918 RMnode and RMvertex3D objects are NOT NULL, then RM_CHILL is returned,
3919 and the RMnode's center point attribute is copied into caller-supplied
3920 memory. Otherwise, RM_WHACKED is returned, and the caller-supplied
3921 memory remains undisturbed.
3922
3923 @dend
3924 * ----------------------------------------------------
3925 */
3926 RMenum
rmNodeGetCenter(const RMnode * n,RMvertex3D * ret)3927 rmNodeGetCenter (const RMnode *n,
3928 RMvertex3D *ret)
3929 {
3930 if ((RM_ASSERT(n, "rmNodeGetCenter() error: the input RMnode pointer is NULL. ") == RM_WHACKED) ||
3931 (RM_ASSERT(ret, "rmNodeGetCenter() error: the return RMvertex3D pointer is NULL.") == RM_WHACKED))
3932 return(RM_WHACKED);
3933
3934 #if 0
3935 /* changed on 1/8/2001 */
3936 if (n->transforms == NULL)
3937 return(RM_WHACKED);
3938
3939 *ret = n->transforms->center;
3940 #endif
3941 *ret = n->center;
3942
3943 return(RM_CHILL);
3944
3945 }
3946
3947
3948 /*
3949 * ----------------------------------------------------
3950 * @Name rmNodeSetScaleMatrix
3951 @pstart
3952 RMenum rmNodeSetScaleMatrix (RMnode *toModify,
3953 const RMmatrix *newMatrix)
3954 @pend
3955
3956 @astart
3957 RMnode *toModify - a handle to an RMnode (modified).
3958
3959 const RMmatrix *newMatrix - a handle to a caller-supplied RMmatrix
3960 (input).
3961 @aend
3962
3963 @dstart
3964
3965 Use this routine to set the "pre-rotation scale matrix"
3966 transformation attribute of an RMnode.
3967
3968 See rmNodeGetCompositeModelMatrix() for a discussion of how the
3969 transformation attributes of an RMnode are combined into a single
3970 transformation.
3971
3972 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
3973
3974 @dend
3975 * ----------------------------------------------------
3976 */
3977 RMenum
rmNodeSetScaleMatrix(RMnode * n,const RMmatrix * newMatrix)3978 rmNodeSetScaleMatrix (RMnode *n,
3979 const RMmatrix *newMatrix)
3980 {
3981 if ((RM_ASSERT(n, "rmNodeSetScaleMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
3982 (RM_ASSERT(newMatrix, "rmNodeSetScaleMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
3983 return(RM_WHACKED);
3984
3985 if (n->transforms == NULL)
3986 n->transforms = private_rmNodeTransformsNew();
3987
3988 n->transforms->s = *newMatrix;
3989
3990 return(RM_CHILL);
3991 }
3992
3993
3994 /*
3995 * ----------------------------------------------------
3996 * @Name rmNodeGetScaleMatrix
3997 @pstart
3998 RMenum rmNodeGetScaleMatrix (const RMnode *toQuery,
3999 RMmatrix *matrixReturn)
4000 @pend
4001
4002 @astart
4003 const RMnode *toQuery - a handle to the RMnode to query (input).
4004
4005 RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
4006 (modified).
4007 @aend
4008
4009 @dstart
4010
4011 If the RMnode toQuery has transformation attributes defined, the
4012 "pre-rotate scale matrix" is copied into caller-supplied memory, and
4013 RM_CHILL is returned to the caller. Otherwise, RM_WHACKED is returned
4014 and the caller-supplied memory remains undisturbed.
4015
4016 @dend
4017 * ----------------------------------------------------
4018 */
4019 RMenum
rmNodeGetScaleMatrix(const RMnode * n,RMmatrix * ret)4020 rmNodeGetScaleMatrix (const RMnode *n,
4021 RMmatrix *ret)
4022 {
4023 if ((RM_ASSERT(n, "rmNodeGetScaleMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
4024 (RM_ASSERT(ret, "rmNodeGetScaleMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
4025 return(RM_WHACKED);
4026
4027 if (n->transforms == NULL)
4028 return(RM_WHACKED);
4029
4030 *ret = n->transforms->s;
4031
4032 return(RM_CHILL);
4033 }
4034
4035
4036 /*
4037 * ----------------------------------------------------
4038 * @Name rmNodeSetRotateMatrix
4039 @pstart
4040 RMenum rmNodeSetRotateMatrix (RMnode *toModify,
4041 const RMmatrix *newMatrix)
4042 @pend
4043
4044 @astart
4045 RMnode *toModify - a handle to an RMnode (modified).
4046
4047 const RMmatrix *newMatrix - a handle to a caller-supplied RMmatrix
4048 (input).
4049 @aend
4050
4051 @dstart
4052
4053 Use this routine to set the "pre-rotation scale matrix"
4054 transformation attribute of an RMnode.
4055
4056 See rmNodeGetCompositeModelMatrix() for a discussion of how the
4057 transformation attributes of an RMnode are combined into a single
4058 transformation.
4059
4060 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
4061
4062 @dend
4063 * ----------------------------------------------------
4064 */
4065 RMenum
rmNodeSetRotateMatrix(RMnode * n,const RMmatrix * newMatrix)4066 rmNodeSetRotateMatrix (RMnode *n,
4067 const RMmatrix *newMatrix)
4068 {
4069 if ((RM_ASSERT(n, "rmNodeSetRotateMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
4070 (RM_ASSERT(newMatrix, "rmNodeSetRotateMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
4071 return(RM_WHACKED);
4072
4073 if (n->transforms == NULL)
4074 n->transforms = private_rmNodeTransformsNew();
4075
4076 n->transforms->r = *newMatrix;
4077
4078 return(RM_CHILL);
4079 }
4080
4081
4082 /*
4083 * ----------------------------------------------------
4084 * @Name rmNodeGetRotateMatrix
4085 @pstart
4086 RMenum rmNodeGetRotateMatrix (const RMnode *toQuery,
4087 RMmatrix *matrixReturn)
4088 @pend
4089
4090 @astart
4091 const RMnode *toQuery - a handle to the RMnode to query (input).
4092
4093 RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
4094 (modified).
4095 @aend
4096
4097 @dstart
4098
4099 If the RMnode toQuery has transformation attributes defined, the
4100 RMnode's rotation matrix transformation attribute is copied into
4101 caller-supplied memory, and RM_CHILL is returned to the
4102 caller. Otherwise, RM_WHACKED is returned and the caller-supplied
4103 memory remains undisturbed.
4104
4105 @dend
4106 * ----------------------------------------------------
4107 */
4108 RMenum
rmNodeGetRotateMatrix(const RMnode * n,RMmatrix * ret)4109 rmNodeGetRotateMatrix (const RMnode *n,
4110 RMmatrix *ret)
4111 {
4112 if ((RM_ASSERT(n, "rmNodeGetRotateMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
4113 (RM_ASSERT(ret, "rmNodeGetRotateMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
4114 return(RM_WHACKED);
4115
4116 if (n->transforms == NULL)
4117 return(RM_WHACKED);
4118
4119 *ret = n->transforms->r;
4120
4121 return(RM_CHILL);
4122 }
4123
4124
4125 /*
4126 * ----------------------------------------------------
4127 * @Name rmNodeSetPostRotateScaleMatrix
4128 @pstart
4129 RMenum rmNodeSetPostRotateScaleMatrix (RMnode *toModify,
4130 const RMmatrix *newMatrix)
4131 @pend
4132
4133 @astart
4134 RMnode *toModify - a handle to an RMnode (modified).
4135
4136 const RMmatrix *newMatrix - a handle to a caller-supplied RMmatrix
4137 (input).
4138 @aend
4139
4140 @dstart
4141
4142 Use this routine to set the "post-rotation scale matrix"
4143 transformation attribute of an RMnode.
4144
4145 See rmNodeGetCompositeModelMatrix() for a discussion of how the
4146 transformation attributes of an RMnode are combined into a single
4147 transformation.
4148
4149 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
4150
4151 @dend
4152 * ----------------------------------------------------
4153 */
4154 RMenum
rmNodeSetPostRotateScaleMatrix(RMnode * n,const RMmatrix * newMatrix)4155 rmNodeSetPostRotateScaleMatrix (RMnode *n,
4156 const RMmatrix *newMatrix)
4157 {
4158 if ((RM_ASSERT(n, "rmNodeSetPostRotateScaleMatrix() error: the input RMnode pointer is NULL. ") == RM_WHACKED) ||
4159 (RM_ASSERT(newMatrix, "rmNodeSetPostRotateScaleMatrix() error: the RMmatrix pointer is NULL.") == RM_WHACKED))
4160 return(RM_WHACKED);
4161
4162 if (n->transforms == NULL)
4163 n->transforms = private_rmNodeTransformsNew();
4164
4165 n->transforms->s2 = *newMatrix;
4166
4167 return(RM_CHILL);
4168 }
4169
4170
4171 /*
4172 * ----------------------------------------------------
4173 * @Name rmNodeGetPostRotateScaleMatrix
4174 @pstart
4175 RMenum rmNodeGetPostRotateScaleMatrix (const RMnode *toQuery,
4176 RMmatrix *matrixReturn)
4177 @pend
4178
4179 @astart
4180 const RMnode *toQuery - a handle to the RMnode to query (input).
4181
4182 RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
4183 (modified).
4184 @aend
4185
4186 @dstart
4187
4188 If the RMnode toQuery has transformation attributes defined, the
4189 "post rotate scale matrix" is copied into caller-supplied memory, and
4190 RM_CHILL is returned to the caller. Otherwise, RM_WHACKED is returned
4191 and the caller-supplied memory remains undisturbed.
4192
4193 @dend
4194 * ----------------------------------------------------
4195 */
4196 RMenum
rmNodeGetPostRotateScaleMatrix(const RMnode * n,RMmatrix * ret)4197 rmNodeGetPostRotateScaleMatrix (const RMnode *n,
4198 RMmatrix *ret)
4199 {
4200 if ((RM_ASSERT(n, "rmNodeGetPostRotateScaleMatrix() error: the input RMnode pointer is NULL. ") == RM_WHACKED) ||
4201 (RM_ASSERT(ret, "rmNodeGetPostRotateScaleMatrix() error: the return RMmatrix pointer is NULL.") == RM_WHACKED))
4202 return(RM_WHACKED);
4203
4204 if (n->transforms == NULL)
4205 return(RM_WHACKED);
4206
4207 *ret = n->transforms->s2;
4208
4209 return(RM_CHILL);
4210 }
4211
4212
4213 /*
4214 * ----------------------------------------------------
4215 * @Name rmNodeSetTranslateVector
4216 @pstart
4217 RMenum rmNodeSetTranslateVector (RMnode *toModify,
4218 const RMvertex3D *newVector)
4219 @pend
4220
4221 @astart
4222 RMnode *toModify - a handle to an RMnode (modified).
4223
4224 const RMvertex3D *newVector - a handle to an RMvertex3D object
4225 (input).
4226 @aend
4227
4228 @dstart
4229
4230 Use this routine to set the "translate vector" transformation
4231 attribute in an RMnode. Returns RM_CHILL upon success, or RM_WHACKED
4232 upon failure.
4233
4234 See rmNodeGetCompositeModelMatrix() for information concerning how
4235 the composite transformation matrix at an RMnode is derived from the
4236 transformation attributes.
4237
4238 @dend
4239 * ----------------------------------------------------
4240 */
4241 RMenum
rmNodeSetTranslateVector(RMnode * n,const RMvertex3D * newVector)4242 rmNodeSetTranslateVector (RMnode *n,
4243 const RMvertex3D *newVector)
4244 {
4245 if ((RM_ASSERT(n, "rmNodeSetTranslateVector() error: the input RMnode pointer is NULL. ") == RM_WHACKED) ||
4246 (RM_ASSERT(newVector, "rmNodeSetTranslateVector() error: the RMvertex3D pointer is NULL.") == RM_WHACKED))
4247 return(RM_WHACKED);
4248
4249 if (n->transforms == NULL)
4250 n->transforms = private_rmNodeTransformsNew();
4251
4252 n->transforms->translate = *newVector;
4253
4254 return(RM_CHILL);
4255
4256 }
4257
4258
4259 /*
4260 * ----------------------------------------------------
4261 * @Name rmNodeGetTranslateVector
4262 @pstart
4263 RMenum rmNodeGetTranslateVector (const RMnode *toQuery,
4264 RMvertex3D *returnVector)
4265 @pend
4266
4267 @astart
4268 const RMnode *toQuery - a handle to an RMnode (input).
4269
4270 RMvertex3D *returnVector - a handle to a caller-supplied RMvertex3D
4271 object (modified).
4272 @aend
4273
4274 @dstart
4275
4276 Use this routine to obtain the translation vector transformation
4277 attribute from an RMnode. If the RMnode has no transformation
4278 attributes, or if the input RMnode or RMvertex3D objects are NULL,
4279 RM_WHACKED is returned. Otherwise, RM_CHILL is returned, and the
4280 translation vector is copied into caller supplied memory.
4281
4282 See rmNodeGetCompositeModelMatrix() for more details concerning how
4283 the RMnode transformation attributes are combined into a single,
4284 composite transformation.
4285
4286 @dend
4287 * ----------------------------------------------------
4288 */
4289 RMenum
rmNodeGetTranslateVector(const RMnode * n,RMvertex3D * ret)4290 rmNodeGetTranslateVector (const RMnode *n,
4291 RMvertex3D *ret)
4292 {
4293 if ((RM_ASSERT(n, "rmNodeGetTranslateVector() error: the input RMnode pointer is NULL. ") == RM_WHACKED) ||
4294 (RM_ASSERT(ret, "rmNodeGetTranslateVector() error: the return RMvertex3D pointer is NULL.") == RM_WHACKED))
4295 return(RM_WHACKED);
4296
4297 if (n->transforms == NULL)
4298 return(RM_WHACKED);
4299
4300 *ret = n->transforms->translate;
4301
4302 return(RM_CHILL);
4303 }
4304
4305
4306 /*
4307 * ----------------------------------------------------
4308 * @Name rmNodeSetPostMatrix
4309 @pstart
4310 RMenum rmNodeSetPostMatrix (RMnode *toModify,
4311 const RMmatrix *newMatrix)
4312 @pend
4313
4314 @astart
4315 RMnode *toModify - a handle to an RMnode (modified).
4316
4317 const RMmatrix *newMatrix - a handle to a caller-supplied RMmatrix
4318 (input).
4319 @aend
4320
4321 @dstart
4322
4323 Use this routine to set the "post matrix" transformation attribute of
4324 an RMnode.
4325
4326 See rmNodeGetCompositeModelMatrix() for a discussion of how the
4327 transformation attributes of an RMnode are combined into a single
4328 transformation.
4329
4330 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
4331
4332 @dend
4333 * ----------------------------------------------------
4334 */
4335 RMenum
rmNodeSetPostMatrix(RMnode * n,const RMmatrix * newMatrix)4336 rmNodeSetPostMatrix (RMnode *n,
4337 const RMmatrix *newMatrix)
4338 {
4339 if ((RM_ASSERT(n, "rmNodeSetPostMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
4340 (RM_ASSERT(newMatrix, "rmNodeSetPostMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
4341 return(RM_WHACKED);
4342
4343 if (n->transforms == NULL)
4344 n->transforms = (internals_RMtransformationStruct *)private_rmNodeTransformsNew();
4345
4346 n->transforms->post = *newMatrix;
4347
4348 return(RM_CHILL);
4349 }
4350
4351
4352 /*
4353 * ----------------------------------------------------
4354 * @Name rmNodeGetPostMatrix
4355 @pstart
4356 RMenum rmNodeGetPostMatrix (const RMnode *toQuery,
4357 RMmatrix *matrixReturn)
4358 @pend
4359
4360 @astart
4361 const RMnode *toQuery - a handle to the RMnode to query (input).
4362
4363 RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
4364 (modified).
4365 @aend
4366
4367 @dstart
4368
4369 If the RMnode toQuery has transformation attributes defined, the
4370 "post matrix" is copied into caller-supplied memory, and RM_CHILL is
4371 returned to the caller. Otherwise, RM_WHACKED is returned and the
4372 caller-supplied memory remains undisturbed.
4373
4374 @dend
4375 * ----------------------------------------------------
4376 */
4377 RMenum
rmNodeGetPostMatrix(const RMnode * n,RMmatrix * ret)4378 rmNodeGetPostMatrix (const RMnode *n,
4379 RMmatrix *ret)
4380 {
4381 if ((RM_ASSERT(n, "rmNodeGetPostMatrix() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
4382 (RM_ASSERT(ret, "rmNodeGetPostMatrix() error: the input RMmatrix pointer is NULL") == RM_WHACKED))
4383 return(RM_WHACKED);
4384
4385 if (n->transforms == NULL)
4386 return(RM_WHACKED);
4387
4388 *ret = n->transforms->post;
4389
4390 return(RM_CHILL);
4391 }
4392
4393
4394 /*
4395 * ----------------------------------------------------
4396 * @Name rmNodeGetCompositeModelMatrix
4397 @pstart
4398 RMenum rmNodeGetCompositeModelMatrix (RMnode *toQuery,
4399 RMmatrix *matrixReturn)
4400 @pend
4401
4402 @astart
4403 RMnode *toQuery - a handle to an RMnode (input).
4404
4405 RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
4406 (return).
4407 @aend
4408
4409 @dstart
4410
4411 Use this routine to obtain the composite transformation of all
4412 transformation matrices in an RMnode. Since transformations are
4413 optional node attributes, if no transformation attributes are present
4414 in an RMnode, the matrixReturn parameter is set to the Identity
4415 matrix.
4416
4417 Upon success, RM_CHILL is returned and the node's composite
4418 transformation matrix is copied into caller-supplied memory (or the
4419 identity matrix is copied into caller-supplied memory if no
4420 transformation attributes are present in the input
4421 RMnode). Otherwise, RM_WHACKED is returned.
4422
4423 The composite transformation is the matrix product of:
4424
4425 [Pre -C S R S2 C T Post]
4426
4427 Where:
4428
4429 Pre (rmNodeSetPreMatrix, rmNodeGetPreMatrix) is the "pre
4430 transformation" matrix applied prior to any other transformations.
4431
4432 -C and C are matrices representing a translation. C represents the
4433 RMnode's "center point" attribute (rmNodeSetCenter, rmNodeGetCenter).
4434
4435 S is the "pre rotate" scale matrix (rmNodeSetScaleMatrix,
4436 rmNodeGetScaleMatrix)
4437
4438 R is the RMnode's rotation matrix (rmNodeSetRotateMatrix,
4439 rmNodeGetRotateMatrix).
4440
4441 S2 is the RMnode's "post rotate" scale matrix
4442 (rmNodeSetPostRotateScaleMatrix, rmNodeGetPostRotateScaleMatrix).
4443
4444 T is a translation matrix derived from the RMnode's translation
4445 vector (rmNodeSetTranslateVector, rmNodeGetTranslateVector).
4446
4447 Post is a transformation matrix applied after all other
4448 transformations at an RMnode (rmNodeSetPostMatrix,
4449 rmNodeGetPostMatrix).
4450
4451 @dend
4452 * ----------------------------------------------------
4453 */
4454 RMenum
rmNodeGetCompositeModelMatrix(RMnode * r,RMmatrix * m)4455 rmNodeGetCompositeModelMatrix (RMnode *r,
4456 RMmatrix *m)
4457 {
4458 RMmatrix minusC, SR, C, T, composite;
4459 RMvertex3D center, translate;
4460
4461 if ((RM_ASSERT(r, "rmNodeGetCompositeModelMatrix() error: the input RMnode is NULL") == RM_WHACKED) ||
4462 (RM_ASSERT(m, "rmNodeGetCompositeModelMatrix() error: the return RMmatrix is NULL") == RM_WHACKED))
4463 return(RM_WHACKED);
4464
4465 if (r->transforms == NULL)
4466 {
4467 rmMatrixIdentity(m);
4468 return(RM_CHILL);
4469 }
4470
4471 rmMatrixIdentity(&minusC);
4472 rmMatrixIdentity(&SR);
4473 rmMatrixIdentity(&C);
4474 rmMatrixIdentity(&T);
4475 rmMatrixIdentity(&composite);
4476
4477 rmNodeGetCenter(r, ¢er);
4478 rmNodeGetTranslateVector(r, &translate);
4479
4480 minusC.m[3][0] = -1.0F * center.x;
4481 minusC.m[3][1] = -1.0F * center.y;
4482 minusC.m[3][2] = -1.0F * center.z;
4483
4484 rmMatrixMultiply(&(r->transforms->s), &(r->transforms->r), &SR);
4485 rmMatrixMultiply(&SR, &(r->transforms->s2), &SR);
4486
4487 C.m[3][0] = center.x;
4488 C.m[3][1] = center.y;
4489 C.m[3][2] = center.z;
4490
4491 T.m[3][0] = translate.x;
4492 T.m[3][1] = translate.y;
4493 T.m[3][2] = translate.z;
4494
4495 rmMatrixMultiply(&(r->transforms->pre), &minusC, &minusC); /* 5/10/99 */
4496 rmMatrixMultiply(&minusC, &SR, &composite);
4497 rmMatrixMultiply(&composite, &C,&composite);
4498 rmMatrixMultiply(&composite, &T,&composite);
4499 rmMatrixMultiply(&composite, &(r->transforms->post), m); /* 5/10/99 */
4500
4501 return(RM_CHILL);
4502 }
4503
4504
4505 /* node callbacks */
4506
4507 /*
4508 * ----------------------------------------------------
4509 * @Name rmNodeSetRenderOrderCallback
4510 @pstart
4511 RMenum rmNodeSetRenderOrderCallback (RMnode *toModify,
4512 int (*appFunc)(const RMnode *, const RMstate *, int *orderIndices, int nChildren))
4513 @pend
4514
4515 @astart
4516 RMnode *toModify - a handle to an RMnode (modified).
4517
4518 int (*appFunc)(const RMnode *, const RMstate *, int *, int) - A handle to an
4519 application callback, or NULL (input).
4520 @aend
4521
4522 @dstart
4523
4524 Use this routine to assign a "render order callback" at an RMnode. To
4525 remove a switch callback from an RMnode, call this routine with a
4526 value of NULL for the appFunc parameter.
4527
4528 This callback, if present, is invoked ONLY from within the
4529 RM_VIEW stage of multistage rendering. There is no equivalent
4530 routine for the RM_DRAW stage of multistage rendering.
4531
4532 During normal traversal, all children of an RMnodes are
4533 traversed. The render order callback is used to alter this behavior: the
4534 presence of a render order callback means that the application has the
4535 opportunity to alter the order in which the children of the node are
4536 renderered. Usually, children are rendered in the order they were added
4537 to the RMnode.
4538
4539 The render order callback provided by the application will be passed an
4540 array of integers that contain the values of 0, 1, ... n-1 where n is the
4541 number of children at the RMnode toModify. These values represent the
4542 indices arranged in the order the children will be rendered. The application
4543 may modify the placement of these indices so that children need not be
4544 rendered in index-order.
4545
4546 In addition to modifying the indices contained in the orderIndices array,
4547 the application should also return an integer value that specifies how
4548 many of the children will be rendered. This value should be in the range
4549 zero through nchildren-1. A value of zero means that no children will
4550 be rendered, and a value of nchildren-1 means that all children will be
4551 rendered, but using the index order defined in the array
4552 orderIndices.
4553
4554 Note that RM will process the indices in the orderIndices array in
4555 increasing order. In other words, RM will render the child referenced
4556 by orderIndices[0] first, then orderIndices[1] next, and so on, until
4557 the application-specified number of children have been processed.
4558
4559 This routine first appeared in v1.4.3.
4560
4561 @dend
4562 * ----------------------------------------------------
4563 */
rmNodeSetRenderOrderCallback(RMnode * toModify,int (* appFunc)(const RMnode *,const RMstate *,int * orderIndices,int nChildren))4564 RMenum rmNodeSetRenderOrderCallback (RMnode *toModify,
4565 int (*appFunc)(const RMnode *, const RMstate *, int *orderIndices, int nChildren))
4566 {
4567 /* check for input null node */
4568 if (RM_ASSERT(toModify, "rmNodeSetRenderOrderCallBack() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4569 return(RM_WHACKED);
4570
4571 /* check for non-null switch callback && non null appFunc, which would
4572 be an error condition - can't have both switch and render order
4573 callbacks at the same time. */
4574
4575 if ((appFunc != NULL) && (toModify->viewSwitchCallback != NULL))
4576 {
4577 rmWarning("rmNodeSetRenderOrderCallback() warning: the input node already has a switch callback, and it is an error condition to set a render order callback to a non-null value when there is a non-null switch callback. First set the switch callback to NULL prior to assigning a render order callback.");
4578 return(RM_WHACKED);
4579 }
4580
4581 toModify->viewRenderOrderCallback = appFunc;
4582
4583 return(RM_CHILL);
4584 }
4585
4586
4587 /*
4588 * ----------------------------------------------------
4589 * @Name rmNodeSetSwitchCallback
4590 @pstart
4591 RMenum rmNodeSetSwitchCallback (RMnode *toModify,
4592 int (*appFunc)(const RMnode *, const RMstate *))
4593 @pend
4594
4595 @astart
4596 RMnode *toModify - a handle to an RMnode (modified).
4597
4598 int (*appFunc)(const RMnode *, const RMstate *) - A handle to an
4599 application callback, or NULL (input).
4600 @aend
4601
4602 @dstart
4603
4604 Use this routine to assign a "switch callback" at an RMnode. To
4605 remove a switch callback from an RMnode, call this routine with a
4606 value of NULL for the appFunc parameter.
4607
4608 This callback, if present, is invoked ONLY from within the
4609 RM_VIEW stage of multistage rendering. There is no equivalent
4610 routine for the RM_DRAW stage of multistage rendering.
4611
4612 During normal traversal, all children of an RMnodes are
4613 traversed. The switch callback is used to alter this behavior: the
4614 presence of a switch callback means that one, and only one of the
4615 child nodes will be traversed. The switch callback returns an integer
4616 value that is interpreted as an index, indicating which of an
4617 RMnode's children to traverse. Should the returned index be "out of
4618 range" (less than zero or greater than or equal to the number of
4619 children nodes), no children nodes will be processed. However, such a
4620 condition is indicative of an application error.
4621
4622 Use an RMnode's "pre traversal" callback to inhibit traversal rather
4623 than returning an out-of-bounds index from the switch callback.
4624
4625 The most common use for the switch callback is to perform
4626 level-of-detail (LOD) model switching based upon view dependent
4627 operations. The application callback is provided two parameters: a
4628 handle to the RMnode owning the switch callback, and an RMstate
4629 object. The RMstate object contains the current model-view and
4630 projection matrices, their inverses, and all other rendering
4631 parameters current at the RMnode "toModify" during a render time
4632 traversal of the scene graph.
4633
4634 See the rmStateGet*() family of routines for more details about the
4635 type of render state information available to application callbacks.
4636
4637 @dend
4638 * ----------------------------------------------------
4639 */
4640 RMenum
rmNodeSetSwitchCallback(RMnode * n,int (* appfunc)(const RMnode *,const RMstate *))4641 rmNodeSetSwitchCallback (RMnode *n,
4642 int (*appfunc)(const RMnode *, const RMstate *))
4643 {
4644 if (RM_ASSERT(n, "rmNodeSetSwitchCallBack() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4645 return(RM_WHACKED);
4646
4647 if ((appfunc != NULL) && (n->viewRenderOrderCallback != NULL))
4648 {
4649 rmWarning("rmNodeSetSwitchCallback() warning: the input node already has a render order callback, and it is an error condition to set a switch callback to a non-null value when there is a non-null render order callback. First set the render order callback to NULL prior to assigning a switch callback.");
4650 return(RM_WHACKED);
4651 }
4652
4653 n->viewSwitchCallback = appfunc;
4654
4655 return(RM_CHILL);
4656 }
4657
4658
4659 /*
4660 * ----------------------------------------------------
4661 * @Name rmNodeSetPreTraversalCallback
4662 @pstart
4663 RMenum rmNodeSetPreTraversalCallback (RMnode *toModify,
4664 RMenum whichPass,
4665 int (*appFunc)(const RMnode *, const RMstate *))
4666 @pend
4667
4668 @astart
4669 RMnode *toModify - a handle to an RMnode (modified).
4670
4671 RMenum whichPass - an RMenum value that specifies during which stage
4672 of multistage rendering the callback should be invoked. Use either
4673 RM_VIEW or RM_RENDER.
4674
4675 int (*appFunc)(const RMnode *, const RMstate *) - A handle to an
4676 application callback, or NULL (input).
4677 @aend
4678
4679 @dstart
4680
4681 Use this routine to assign a "pre-traversal callback" at an
4682 RMnode. To remove a pre-traversal callback from an RMnode, call this
4683 routine with a value of NULL for the appFunc parameter.
4684
4685 The callback will be invoked during the view pass if RM_VIEW
4686 is specified for the "whichPass", or during the rendering pass
4687 if RM_RENDER is specified. The view pass precedes the rendering
4688 pass, and is where all view-dependent operations should be
4689 performed (distance-based model switching, frustum culling, etc).
4690 Only drawing should be performed during the rendering pass.
4691
4692 During normal scene graph traversal, all children of an RMnodes are
4693 traversed. The pre-traversal callback is used to possibly alter this
4694 behavior: upon entry to an RMnode, the pre-traversal callback is
4695 invoked prior to any other RMnode processing. If the pre-traversal
4696 callback returns a positive value (greater than zero), the RMnode is
4697 processed "as usual." However, if the pre-traversal callback returns
4698 a value that is less than or equal to zero, processing of the RMnode
4699 terminates; no children will be processed, and any other callbacks
4700 in the RMnode (switch, post-traversal) will NOT be invoked.
4701
4702 The application callback is provided two parameters: a handle to the
4703 RMnode owning the switch callback, and an RMstate object. The RMstate
4704 object contains the current model-view and projection matrices, their
4705 inverses, and all other rendering parameters current at the RMnode
4706 "toModify" during a render time traversal of the scene graph.
4707
4708 The pretraversal callback is invoked *before* any scene parameters
4709 are processed at the given node. Applications that use the pretraversal
4710 callback to implement direct OpenGL rendering need to be aware that
4711 no scene parameters contained in the node will be processed. An alternate
4712 way to accomplish implementation of application rendering code that takes
4713 advantage of any scene parameters contained in the node along with the
4714 application callback is to use the post-traversal callback.
4715
4716 Important note about RM_RENDER pre traversal callbacks: the return value
4717 provided by the application-supplied render-stage pre-traversal callback
4718 is ignored as of the time of this writing (8/11/02). This means that the
4719 render-stage traversal callback will be invoked during the render stage,
4720 but will not alter the scene graph traversal algorithm. This incorrect
4721 behavior will be fixed in a later release.
4722
4723 See the rmStateGet*() family of routines for more details about the
4724 type of render state information available to application callbacks.
4725
4726 @dend
4727 * ----------------------------------------------------
4728 */
4729 RMenum
rmNodeSetPreTraversalCallback(RMnode * n,RMenum whichPass,int (* appfunc)(const RMnode *,const RMstate *))4730 rmNodeSetPreTraversalCallback (RMnode *n,
4731 RMenum whichPass,
4732 int (*appfunc)(const RMnode *, const RMstate *))
4733 {
4734 if (RM_ASSERT(n, "rmNodeSetPreTraverseCallBack() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4735 return(RM_WHACKED);
4736
4737 if ((whichPass != RM_VIEW) && (whichPass != RM_RENDER))
4738 {
4739 rmError("rmNodeSetPreTraversalCallback error: the input \"whichPass\" enumerator is neither RM_VIEW nor RM_RENDER. The scene graph node remains unmodified. ");
4740 return(RM_WHACKED);
4741 }
4742
4743 if (whichPass == RM_VIEW)
4744 n->viewPretraverseCallback = appfunc;
4745 else /* assume RM_RENDER */
4746 n->renderPretraverseCallback = appfunc;
4747
4748 return(RM_CHILL);
4749 }
4750
4751
4752 /*
4753 * ----------------------------------------------------
4754 * @Name rmNodeSetPostTraversalCallback
4755 @pstart
4756 RMenum rmNodeSetPostTraversalCallback (RMnode *toModify,
4757 RMenum whichPass,
4758 int (*appFunc)(const RMnode *, const RMstate *))
4759 @pend
4760
4761 @astart
4762 RMnode *toModify - a handle to an RMnode (modified).
4763
4764 RMenum whichPass - an RMenum value that specifies during which stage
4765 of multistage rendering the callback should be invoked. Use either
4766 RM_VIEW or RM_RENDER.
4767
4768 int (*appFunc)(const RMnode *, const RMstate *) - A handle to an
4769 application callback, or NULL (input).
4770 @aend
4771
4772 @dstart
4773
4774 Use this routine to assign a "post-traversal callback" at an
4775 RMnode. To remove a post-traversal callback from an RMnode, call this
4776 routine with a value of NULL for the appFunc parameter.
4777
4778 The callback will be invoked during the view pass if RM_VIEW
4779 is specified for the "whichPass", or during the rendering pass
4780 if RM_RENDER is specified. The view pass precedes the rendering
4781 pass, and is where all view-dependent operations should be
4782 performed (distance-based model switching, frustum culling, etc).
4783 Only drawing should be performed during the rendering pass.
4784
4785 The post-traversal callback does not affect processing of children
4786 nodes. It is simply a hook provided to applications that is invoked
4787 after all other processing at an RMnode is complete, including
4788 traversal of all an RMnode's children.
4789
4790 The return value from the post traversal callback is ignored.
4791
4792 The application callback is provided two parameters: a handle to the
4793 RMnode owning the switch callback, and an RMstate object. The RMstate
4794 object contains the current model-view and projection matrices, their
4795 inverses, and all other rendering parameters current at the RMnode
4796 "toModify" during a render time traversal of the scene graph.
4797
4798 See the rmStateGet*() family of routines for more details about the
4799 type of render state information available to application callbacks.
4800
4801 @dend
4802 * ----------------------------------------------------
4803 */
4804 RMenum
rmNodeSetPostTraversalCallback(RMnode * n,RMenum whichPass,int (* appfunc)(const RMnode *,const RMstate *))4805 rmNodeSetPostTraversalCallback (RMnode *n,
4806 RMenum whichPass,
4807 int (*appfunc)(const RMnode *, const RMstate *))
4808 {
4809 if (RM_ASSERT(n, "rmNodePostTraversalCallBack() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4810 return(RM_WHACKED);
4811
4812 if ((whichPass != RM_VIEW) && (whichPass != RM_RENDER))
4813 {
4814 rmError("rmNodeSetPostTraversalCallback error: the input \"whichPass\" enumerator is neither RM_VIEW nor RM_RENDER. The scene graph node remains unmodified. ");
4815 return(RM_WHACKED);
4816 }
4817
4818 if (whichPass == RM_VIEW)
4819 n->viewPosttraverseCallback = appfunc;
4820 else /* assume RM_RENDER */
4821 n->renderPosttraverseCallback = appfunc;
4822
4823 return(RM_CHILL);
4824 }
4825
4826 /*
4827 * ----------------------------------------------------
4828 * @Name rmNodeMutexInit
4829 @pstart
4830 RMenum rmNodeMutexInit(RMnode *toModify,
4831 RMenum initialLockState)
4832 @pend
4833
4834 @astart
4835 RMnode *toModify - a handle to an RMnode (input).
4836 RMenum initialLockState - an RMenum value (input). May be either
4837 RM_MUTEX_LOCK or RM_MUTEX_UNLOCK.
4838 @aend
4839
4840 @dstart
4841
4842 Creates a new RMmutex object, and assigns it to the RMnode
4843 "toModify." The initial state of the mutex lock is set to the
4844 value "initialLockState." Returns RM_CHILL upon success, or
4845 RM_WHACKED upon failure.
4846
4847 Mutexes are control mechanisms used to synchronize access to
4848 "critical" resources, and are most often used to serialize access
4849 to variables between multiple, concurrent execution threads.
4850 Mutexes are an optional field in an RM scene graph node that
4851 are explicitly created, manipulated and destroyed by applications.
4852 The behavior of the mutex synchronization tools in OpenRM is
4853 modeled closely after that of POSIX threads/semaphores/mutexes.
4854
4855 In OpenRM, the RMmutex is an abstraction layer that uses
4856 POSIX mutex in Unix implementations, and native Win32 methods under
4857 windows. Like the POSIX model, the following guidelines apply:
4858
4859 - Mutexes are created and assigned an initial value.
4860 - Unlocking the mutex is an atomic, async-safe operation.
4861 - Mutex locking is a blocking operation that will suspend the caller
4862 until the mutex is unlocked.
4863 - A non-blocking lock operation is provided via rmNodeMutexTryLock.
4864 @dend
4865 * ----------------------------------------------------
4866 */
4867 RMenum
rmNodeMutexInit(RMnode * n,RMenum initLockStatus)4868 rmNodeMutexInit(RMnode *n,
4869 RMenum initLockStatus)
4870 {
4871 if (RM_ASSERT(n, "rmNodeMutexInit() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4872 return(RM_WHACKED);
4873
4874 if ((n->nodeMutex = rmMutexNew(RM_MUTEX_UNLOCK)) == NULL)
4875 {
4876 rmError("rmNodeMutexInit(): error creating node mutex. \n");
4877 return(RM_WHACKED);
4878 }
4879
4880 if (initLockStatus == RM_MUTEX_LOCK)
4881 rmMutexLock(n->nodeMutex);
4882
4883 return(RM_CHILL);
4884 }
4885
4886 /*
4887 * ----------------------------------------------------
4888 * @Name rmNodeMutexUnlock
4889 @pstart
4890 RMenum rmNodeMutexUnlock (RMnode *toModify)
4891 @pend
4892
4893 @astart
4894 RMnode *toModify - a handle to an RMnode (input).
4895 @aend
4896
4897 @dstart
4898
4899 Performs an atomic "unlock" operation to the RMmutex
4900 object in an RMnode. This operation is non-blocking, and is async-safe.
4901 If no RMmutex exists in the RMnode, or if there is some other
4902 type of error encountered, RM_WHACKED is returned. Otherwise,
4903 RM_CHILL indicates successful completion of the post operation.
4904
4905 Mutexes are control mechanisms used to synchronize access to
4906 "critical" resources, and are most often used to serialize access
4907 to variables between multiple, concurrent execution threads.
4908 Mutexes are an optional field in an RM scene graph node that
4909 are explicitly created, manipulated and destroyed by applications.
4910 The behavior of the mutex synchronization tools in OpenRM is
4911 modeled closely after that of POSIX threads/semaphores/mutexes.
4912
4913 In OpenRM, the RMmutex is an abstraction layer that uses
4914 POSIX mutex in Unix implementations, and native Win32 methods under
4915 windows. Like the POSIX model, the following guidelines apply:
4916
4917 - Mutexes are created and assigned an initial value.
4918 - Unlocking the mutex is an atomic, async-safe operation.
4919 - Mutex locking is a blocking operation that will suspend the caller
4920 until the mutex is unlocked.
4921 - A non-blocking lock operation is provided via rmNodeMutexTryLock.
4922 @dend
4923 * ----------------------------------------------------
4924 */
4925 RMenum
rmNodeMutexUnlock(RMnode * n)4926 rmNodeMutexUnlock(RMnode *n)
4927 {
4928 if (RM_ASSERT(n, "rmNodeMutexUnlock() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4929 return(RM_WHACKED);
4930
4931 return(rmMutexUnlock(n->nodeMutex));
4932 }
4933
4934 /*
4935 * ----------------------------------------------------
4936 * @Name rmNodeMutexLock
4937 @pstart
4938 RMenum rmNodeMutexLock (RMnode *toModify)
4939 @pend
4940
4941 @astart
4942 RMnode *toModify - a handle to an RMnode (input).
4943 @aend
4944
4945 @dstart
4946
4947 Performs a blocking lock operation: execution will be suspended
4948 until the RMmutex of the RMnode "toModify" becomes unlocked.
4949 When the RMmutex becomes unlocked, this routine will lock
4950 the RMmutex, then return. Upon successful completion of the wait
4951 operation, RM_CHILL is returned. Otherwise, RM_WHACKED indicates an
4952 error of some type.
4953
4954 Mutexes are control mechanisms used to synchronize access to
4955 "critical" resources, and are most often used to serialize access
4956 to variables between multiple, concurrent execution threads.
4957 Mutexes are an optional field in an RM scene graph node that
4958 are explicitly created, manipulated and destroyed by applications.
4959 The behavior of the mutex synchronization tools in OpenRM is
4960 modeled closely after that of POSIX threads/semaphores/mutexes.
4961
4962 In OpenRM, the RMmutex is an abstraction layer that uses
4963 POSIX mutex in Unix implementations, and native Win32 methods under
4964 windows. Like the POSIX model, the following guidelines apply:
4965
4966 - Mutexes are created and assigned an initial value.
4967 - Unlocking the mutex is an atomic, async-safe operation.
4968 - Mutex locking is a blocking operation that will suspend the caller
4969 until the mutex is unlocked.
4970 - A non-blocking lock operation is provided via rmNodeMutexTryLock.
4971 @dend
4972 * ----------------------------------------------------
4973 */
4974 RMenum
rmNodeMutexLock(RMnode * n)4975 rmNodeMutexLock(RMnode *n)
4976 {
4977 if (RM_ASSERT(n, "rmNodeMutexLock() error: the input RMnode pointer is NULL.") == RM_WHACKED)
4978 return(RM_WHACKED);
4979
4980 return(rmMutexLock(n->nodeMutex));
4981 }
4982
4983 /*
4984 * ----------------------------------------------------
4985 * @Name rmNodeMutexTryLock
4986 @pstart
4987 RMenum rmNodeMutexTryLock (const RMnode *toQuery)
4988 @pend
4989
4990 @astart
4991 const RMnode *toQuery - a handle to an RMnode (input).
4992 int *returnValue - a handle to a caller-supplied int (return).
4993
4994 @aend
4995
4996 @dstart
4997
4998 Performs a non-blocking lock of the RMmutex in the RMnode
4999 toQuery. Return values are the same as those for rmMutexTryLock.
5000
5001 Mutexes are control mechanisms used to synchronize access to
5002 "critical" resources, and are most often used to serialize access
5003 to variables between multiple, concurrent execution threads.
5004 Mutexes are an optional field in an RM scene graph node that
5005 are explicitly created, manipulated and destroyed by applications.
5006 The behavior of the mutex synchronization tools in OpenRM is
5007 modeled closely after that of POSIX threads/semaphores/mutexes.
5008
5009 In OpenRM, the RMmutex is an abstraction layer that uses
5010 POSIX mutex in Unix implementations, and native Win32 methods under
5011 windows. Like the POSIX model, the following guidelines apply:
5012
5013 - Mutexes are created and assigned an initial value.
5014 - Unlocking the mutex is an atomic, async-safe operation.
5015 - Mutex locking is a blocking operation that will suspend the caller
5016 until the mutex is unlocked.
5017 - A non-blocking lock operation is provided via rmNodeMutexTryLock.
5018 @dend
5019 * ----------------------------------------------------
5020 */
5021 RMenum
rmNodeMutexTryLock(const RMnode * n)5022 rmNodeMutexTryLock(const RMnode *n)
5023 {
5024 if (RM_ASSERT(n, "rmNodeMutexTryLoakc() error: the input RMnode pointer is NULL.") == RM_WHACKED)
5025 return(RM_WHACKED);
5026
5027 return(rmMutexTryLock(n->nodeMutex));
5028 }
5029
5030 /*
5031 * ----------------------------------------------------
5032 * @Name rmNodeGetMutex
5033 @pstart
5034 RMmutex * rmNodeGetMutex (const RMnode *toQuery)
5035 @pend
5036
5037 @astart
5038 const RMnode *toQuery - a handle to an RMnode (input).
5039
5040 @aend
5041
5042 @dstart
5043
5044 Returns to the caller the handle of the RMmutex at the RMnode.
5045 If no RMmutex is present in the RMnode, NULL is returned.
5046 Callers may perform any valid operation upon the RMmutex
5047 (rmMutexLock, rmMutexUnlock or rmMutexTryLock), with
5048 one exception: callers may not destroy the RMnode mutex directly.
5049 To destroy the RMnode mutex, use the routine rmNodeMutexDestroy().
5050
5051 Mutexes are control mechanisms used to synchronize access to
5052 "critical" resources, and are most often used to serialize access
5053 to variables between multiple, concurrent execution threads.
5054 Mutexes are an optional field in an RM scene graph node that
5055 are explicitly created, manipulated and destroyed by applications.
5056 The behavior of the mutex synchronization tools in OpenRM is
5057 modeled closely after that of POSIX threads/semaphores/mutexes.
5058
5059 In OpenRM, the RMmutex is an abstraction layer that uses
5060 POSIX mutex in Unix implementations, and native Win32 methods under
5061 windows. Like the POSIX model, the following guidelines apply:
5062
5063 - Mutexes are created and assigned an initial value.
5064 - Unlocking the mutex is an atomic, async-safe operation.
5065 - Mutex locking is a blocking operation that will suspend the caller
5066 until the mutex is unlocked.
5067 - A non-blocking lock operation is provided via rmNodeMutexTryLock.
5068 @dend
5069 * ----------------------------------------------------
5070 */
5071 RMmutex *
rmNodeGetMutex(const RMnode * n)5072 rmNodeGetMutex(const RMnode *n)
5073 {
5074 if (RM_ASSERT(n, "rmNodeGetMutex() error: the input RMnode pointer is NULL.") == RM_WHACKED)
5075 return(NULL);
5076
5077 return(n->nodeMutex);
5078 }
5079
5080 /*
5081 * ----------------------------------------------------
5082 * @Name rmNodeMutexDelete
5083 @pstart
5084 RMenum rmNodeMutexDelete (RMnode *toModify)
5085 @pend
5086
5087 @astart
5088 RMnode *toModify - a handle to an RMnode (input).
5089 @aend
5090
5091 @dstart
5092
5093 Deletes the RMmutex in the RMnode "toModify." Upon success,
5094 RM_CHILL is returned. A return value of RM_WHACKED indicates some
5095 type of error.
5096
5097 Mutexes are control mechanisms used to synchronize access to
5098 "critical" resources, and are most often used to serialize access
5099 to variables between multiple, concurrent execution threads.
5100 Mutexes are an optional field in an RM scene graph node that
5101 are explicitly created, manipulated and deleted by applications.
5102 The behavior of the mutex synchronization tools in OpenRM is
5103 modeled closely after that of POSIX threads/semaphores/mutexes.
5104
5105 In OpenRM, the RMmutex is an abstraction layer that uses
5106 POSIX mutex in Unix implementations, and native Win32 methods under
5107 windows. Like the POSIX model, the following guidelines apply:
5108
5109 - Mutexes are created and assigned an initial value.
5110 - Unlocking the mutex is an atomic, async-safe operation.
5111 - Mutex locking is a blocking operation that will suspend the caller
5112 until the mutex is unlocked.
5113 - A non-blocking lock operation is provided via rmNodeMutexTryLock.
5114 @dend
5115 * ----------------------------------------------------
5116 */
5117 RMenum
rmNodeMutexDelete(RMnode * n)5118 rmNodeMutexDelete(RMnode *n)
5119 {
5120 RMenum status=RM_CHILL;
5121
5122 if (RM_ASSERT(n, "rmNodeMutexDelete() error: the input RMnode pointer is NULL.") == RM_WHACKED)
5123 return(RM_WHACKED);
5124
5125 if (n->nodeMutex != NULL)
5126 {
5127 status = rmMutexDelete(n->nodeMutex);
5128 n->nodeMutex = NULL;
5129 }
5130 return(status);
5131 }
5132
5133 /*
5134 * ----------------------------------------------------
5135 * @Name rmNodeFrustumCullCallback
5136 @pstart
5137 int rmNodeFrustumCullCallback(const RMnode *n, const RMstate *s)
5138 @pend
5139
5140 @astart
5141 const RMnode *n - an RMnode pointer (input).
5142
5143 const RMstate *s - an RMstate pointer (input).
5144 @aend
5145
5146 @dstart
5147
5148 This routine implements a view frustum cull test, and is not intended
5149 to be called directly by applications. Developers should use this
5150 routine as a parameter to rmNodeSetPretraversalCallback using
5151 RM_VIEW to implement view frustum calling during the view stage
5152 of multistage rendering.
5153
5154 rmNodeFrustumCallback will transform the corners of the RMnode's
5155 bounding box through the composite viewing matrix (model+view+projection)
5156 active at this node during the view traversal (transformations
5157 accumulate!) and will compare the visibility of each of these eight
5158 transformed vertices. If any are visible (lie within the view frustum),
5159 a value of 1 is returned. Otherwise, all vertices lie outside the view
5160 frustum, and a value of zero is returned.
5161
5162 During a view traversal, if a node is culled by this test, this
5163 node and any children will not be further processed - and won't
5164 be rendered during the rendering stage. This type of frustum culling
5165 is an excellent way to reduce load on the graphics engine by performing
5166 view-dependent, model-level simplification.
5167
5168 When this routine is used as indicated in the code example below, the
5169 result if view frustum culling for RMnodes.
5170
5171 Example code:
5172
5173 <pre>
5174
5175 RMnode *n = rmNodeNew("myNode",RM_RENDERPASS_ALL, RM_RENDERPASS_ALL);
5176
5177 ... other node config and primitive building omitted ...
5178
5179 rmNodeComputeBoundingBox(n);
5180
5181 rmNodeSetPretraversalCallback(n, RM_VIEW, rmNodeFrustumCallback);
5182
5183 </pre>
5184
5185 Caveats:
5186
5187 1. There are some circumstances in which if all bounding box
5188 vertices lie outside the view frustum, that the interior of the
5189 box lies inside the view frustum. One example is when the frustum
5190 is completely contained within the bounding box. This routine needs to
5191 be modified to account for these types of conditions (using
5192 a method similar to the Cohen-Sutherland polygon clipping algorithm).
5193
5194 2. In order for this routine to be effective, nodes need to have
5195 bounding boxes. It is up to the application to ensure that bounding
5196 boxes are present at RMnodes for which frustum culling is used.
5197 See rmNodeComputeBoundingBox, rmNodeSetBoundingBox and
5198 rmNodeUnionAllBoxes.
5199
5200 3. This routine has been tested for perspective projections, but
5201 needs to be tested with orthographic projections.
5202
5203 For best results, the size of the bounding box should be < the
5204 size of the frustum.
5205
5206 @dend
5207 * ----------------------------------------------------
5208 */
5209 int
rmNodeFrustumCullCallback(const RMnode * n,const RMstate * s)5210 rmNodeFrustumCullCallback(const RMnode *n,
5211 const RMstate *s)
5212 {
5213 int i;
5214 float v[4],d[4];
5215 RMvertex3D bmin, bmax;
5216 RMvertex3D t[8];
5217 int isVisible;
5218 const RMmatrix *c;
5219
5220 /* get the bounding box. */
5221 if (rmNodeGetBoundingBox(n,&bmin, &bmax) == RM_WHACKED)
5222 return 1; /* not sure what to do - just draw it. */
5223
5224 /* from the two corners of the source box, construct the other
5225 6 of the fully-specified box. we assume the original box is an
5226 axis aligned thing. */
5227 t[0] = bmin;
5228 t[1] = t[0];
5229 t[1].x = bmax.x;
5230 t[2] = t[1];
5231 t[2].y = bmax.y;
5232 t[3] = t[2];
5233 t[3].x = bmin.x;
5234
5235 t[4] = bmin;
5236 t[4].z = bmax.z;
5237 t[5] = t[4];
5238 t[5].x = bmax.x;
5239 t[6] = t[5];
5240 t[6].y = bmax.y;
5241 t[7] = t[6];
5242 t[7].x = bmin.x;
5243
5244 isVisible = 0;
5245 c = &(s->composite) ;
5246
5247 for (i=0;i<8;i++)
5248 {
5249 int mask = (1 << i);
5250
5251 v[0] = t[i].x;
5252 v[1] = t[i].y;
5253 v[2] = t[i].z;
5254 v[3] = 1.F;
5255
5256 /* transform a box vertex through the current
5257 model+view+projection matrix. we include the project matrix
5258 to simplify comparisons in Z. t*/
5259 rmPoint4MatrixTransform(v,c,d);
5260
5261 /* this test is valid for perspective projections. need to
5262 test with orthographic projections. */
5263 if ((d[2] > 0.0) && ( d[2] <= d[3]) && (fabs(d[0]) <= d[3]) && (fabs(d[1]) <= d[3]))
5264 isVisible |= mask;
5265
5266 if (isVisible != 0) /* one of bbox corners is visible */
5267 break;
5268 }
5269
5270 if (isVisible == 0)
5271 return(0);
5272 else
5273 return(1);
5274 }
5275
5276 /* PRIVATE */
5277 void
private_rmSceneGraphWalk(RMnode * r,const RMstate * last,void (* userfunc)(RMnode * node,const RMstate * state,void * clientData),void * clientData)5278 private_rmSceneGraphWalk (RMnode *r,
5279 const RMstate *last,
5280 void (*userfunc)(RMnode *node,
5281 const RMstate *state,
5282 void *clientData),
5283 void *clientData)
5284 {
5285 /*
5286 * walk the scene graph.
5287 * at each node, create the render state and pass that, along
5288 * with the node to the user function. the user function
5289 * may provide a pointer to something, and it is dragged along
5290 * as well.
5291 */
5292 int i;
5293 RMstate *s;
5294 int pushedAttribsReturn = 0;
5295
5296 if (userfunc == NULL)
5297 return;
5298
5299 s = rmStateNew();
5300 rmStateCopy(last, s);
5301
5302 private_collectAndApplyMatrices (s, r, NULL, GL_RENDER,
5303 &pushedAttribsReturn, RM_FALSE);
5304
5305 private_updateSceneParms(r, s, RM_FALSE, 0, NULL, NULL);
5306
5307 (*userfunc)(r, s, clientData);
5308
5309 for (i = 0; i < rmNodeGetNumChildren(r); i++)
5310 private_rmSceneGraphWalk(rmNodeGetIthChild(r, i), s, userfunc, clientData);
5311
5312 rmStateDelete(s);
5313 }
5314
5315
5316 /* PRIVATE */
5317 void
private_rmNodeIncrementRefcount(RMnode * n)5318 private_rmNodeIncrementRefcount (RMnode *n)
5319 {
5320 n->refcount++;
5321 }
5322
5323
5324 /* PRIVATE */
5325 int
private_rmNodeGetRefcount(RMnode * n)5326 private_rmNodeGetRefcount (RMnode *n)
5327 {
5328 return(n->refcount);
5329 }
5330
5331
5332 /* PRIVATE */
5333 int
private_rmNodeDecrementRefcount(RMnode * n)5334 private_rmNodeDecrementRefcount (RMnode *n)
5335 {
5336 return(n->refcount--);
5337 }
5338
5339
5340 /* PRIVATE */
5341 internals_RMtransformationStruct *
private_rmNodeTransformsNew(void)5342 private_rmNodeTransformsNew (void)
5343 {
5344 internals_RMtransformationStruct *t;
5345
5346 t = (internals_RMtransformationStruct *)malloc(sizeof(internals_RMtransformationStruct));
5347
5348 /* initialize all matrices to I and all vectors to zero */
5349 memset(t, 0, sizeof(internals_RMtransformationStruct));
5350
5351 rmMatrixIdentity(&(t->pre));
5352 rmMatrixIdentity(&(t->s));
5353 rmMatrixIdentity(&(t->r));
5354 rmMatrixIdentity(&(t->s2));
5355 rmMatrixIdentity(&(t->post));
5356
5357 #if 0
5358 /* changed 1/8/2001 */
5359 t->center.x = t->center.y = t->center.z = 0.0;
5360 #endif
5361
5362 t->transform_mode = RM_TRANSFORM_GEOMETRY;
5363
5364 return(t);
5365 }
5366
5367
5368 /* PRIVATE */
5369 void
private_rmNodeTransformsDelete(internals_RMtransformationStruct * t)5370 private_rmNodeTransformsDelete (internals_RMtransformationStruct *t)
5371 {
5372 if (t != NULL)
5373 free((void *)t);
5374 }
5375
5376
5377 /* PRIVATE */
5378 void
private_rmPrimTypeToString(RMenum ptype,char * b1)5379 private_rmPrimTypeToString (RMenum ptype,
5380 char *b1)
5381 {
5382 switch(ptype)
5383 {
5384 case RM_LINES:
5385 strcpy(b1,"RM_LINES");
5386 break;
5387
5388 case RM_LINE_STRIP:
5389 strcpy(b1, "RM_LINE_STRIP");
5390 break;
5391
5392 case RM_TRIANGLES:
5393 strcpy(b1, "RM_TRIANGLES");
5394 break;
5395
5396 case RM_TRIANGLE_STRIP:
5397 strcpy(b1, "RM_TRIANGLE_STRIP");
5398 break;
5399
5400 case RM_TRIANGLE_FAN:
5401 strcpy(b1, "RM_TRIANGLE_FAN");
5402 break;
5403
5404 case RM_QUADMESH:
5405 strcpy(b1, "RM_QUADMESH");
5406 break;
5407
5408 case RM_POINTS:
5409 strcpy(b1, "RM_POINTS");
5410 break;
5411
5412 case RM_POLYS:
5413 strcpy(b1,"RM_POLYS");
5414 break;
5415
5416 case RM_QUAD_STRIP:
5417 strcpy(b1,"RM_QUAD_STRIP");
5418 break;
5419
5420 case RM_SPHERES:
5421 strcpy(b1, "RM_SPHERES");
5422 break;
5423
5424 case RM_BOX3D:
5425 strcpy(b1, "RM_BOX3D");
5426 break;
5427
5428 case RM_BOX3D_WIRE:
5429 strcpy(b1, "RM_BOX3D_WIRE");
5430 break;
5431
5432 case RM_CONES:
5433 strcpy(b1, "RM_CONES");
5434 break;
5435
5436 case RM_CYLINDERS:
5437 strcpy(b1, "RM_CYLINDERS");
5438 break;
5439
5440 case RM_OCTMESH:
5441 strcpy(b1, "RM_OCTMESH");
5442 break;
5443
5444 case RM_TEXT:
5445 strcpy(b1, "RM_TEXT");
5446 break;
5447
5448 case RM_INDEXED_TEXT:
5449 strcpy(b1, "RM_INDEXED_TEXT");
5450 break;
5451
5452 case RM_QUADS:
5453 strcpy(b1, "RM_QUADS");
5454 break;
5455
5456 case RM_MARKERS2D:
5457 strcpy(b1, "RM_MARKERS2D");
5458 break;
5459
5460 case RM_CIRCLE2D:
5461 strcpy(b1, "RM_CIRCLE2D");
5462 break;
5463
5464 case RM_BOX2D:
5465 strcpy(b1, "RM_BOX2D");
5466 break;
5467
5468 case RM_ELLIPSE2D:
5469 strcpy(b1, "RM_ELLIPSE2D");
5470 break;
5471
5472 case RM_SPRITE:
5473 strcpy(b1, "RM_SPRITE");
5474 break;
5475
5476
5477 case RM_BITMAP:
5478 strcpy(b1, "RM_BITMAP");
5479 break;
5480
5481 case RM_INDEXED_BITMAP:
5482 strcpy(b1, "RM_INDEXED_BITMAP");
5483 break;
5484
5485 case RM_INDEXED_TFAN:
5486 strcpy(b1,"RM_INDEXED_TFAN");
5487 break;
5488
5489 case RM_INDEXED_QUADS:
5490 strcpy(b1,"RM_INDEXED_QUADS");
5491 break;
5492
5493 case RM_INDEXED_TRIANGLES:
5494 strcpy(b1,"RM_INDEXED_TRIANGLES");
5495 break;
5496
5497 case RM_INDEXED_TRIANGLE_STRIP:
5498 strcpy(b1, "RM_INDEXED_TRIANGLE_STRIP");
5499 break;
5500
5501 case RM_INDEXED_QUAD_STRIP:
5502 strcpy(b1,"RM_INDEXED_QUAD_STRIP");
5503 break;
5504
5505 case RM_APP_DISPLAYLIST:
5506 strcpy(b1,"RM_APP_DISPLAYLIST");
5507 break;
5508
5509 case RM_USERDEFINED_PRIM:
5510 strcpy(b1, "RM_USERDEFINED_PRIM");
5511 break;
5512
5513 default: /* bogus prim enum */
5514 strcpy(b1, "Undefined Primitive type");
5515 break;
5516 }
5517 }
5518
5519 /* PRIVATE */
5520 int
private_rmPrimitiveHackGetNverts(RMprimitive * p)5521 private_rmPrimitiveHackGetNverts (RMprimitive *p)
5522 {
5523 RMprimitiveDataBlob *vblob;
5524
5525 vblob = private_rmBlobFromIndex(p, BLOB_VERTEX_INDEX);
5526
5527 return(private_rmBlobGetNthings(vblob));
5528 }
5529
5530 int
private_rmPrimitiveHackGetNnormals(RMprimitive * p)5531 private_rmPrimitiveHackGetNnormals (RMprimitive *p)
5532 {
5533 RMprimitiveDataBlob *vblob;
5534
5535 vblob = private_rmBlobFromIndex(p, BLOB_NORMAL_INDEX);
5536
5537 return(private_rmBlobGetNthings(vblob));
5538 }
5539
5540 int
private_rmPrimitiveHackGetNcolors(RMprimitive * p)5541 private_rmPrimitiveHackGetNcolors (RMprimitive *p)
5542 {
5543 RMprimitiveDataBlob *vblob;
5544
5545 vblob = private_rmBlobFromIndex(p, BLOB_COLOR_INDEX);
5546
5547 return(private_rmBlobGetNthings(vblob));
5548 }
5549
5550
5551 /* macros */
5552 #define DOTABS(i,f) {int j; for(j = 0; j < i; j++) fprintf((f),"\t");}
5553 #define DOSPACES(i,f) {int j; for(j = 0; j < i; j++) fprintf((f)," ");}
5554
5555
5556 /* PRIVATE
5557 *
5558 * private_rmPrintLight prints an RMlight
5559 * it is little used and probably incomplete.
5560 */
5561 void
private_rmPrintLight(RMlight * l,int level,int i,FILE * f)5562 private_rmPrintLight (RMlight *l,
5563 int level,
5564 int i,
5565 FILE *f)
5566 {
5567 RMcolor4D a, d, s;
5568
5569 DOTABS(level, f);
5570 fprintf(f, "Light Source %d is of type %s \n", i,
5571 (rmLightGetType(l) == RM_LIGHT_POINT ? "RM_LIGHT_POINT" :
5572 (rmLightGetType(l) == RM_LIGHT_DIRECTIONAL ? "RM_LIGHT_DIRECTIONAL" : "RM_LIGHT_SPOT")));
5573
5574 rmLightGetColor(l, &a, &d, &s);
5575 DOTABS(level, f);
5576 fprintf(f, "\tLight color is (amb,diff,spec): (%f %f %f), (%f %f %f), (%f %f %f) \n", a.r, a.g, a.b, d.r, d.g, d.b, s.r, s.g, s.b);
5577 }
5578
5579
5580 /* PRIVATE
5581 *
5582 * private_rmPrintNode prints an RMnode
5583 * it is little used and probably incomplete
5584 */
5585 static void
private_rmPrintNode(const RMnode * r,int level,int pmode,FILE * f)5586 private_rmPrintNode (const RMnode *r,
5587 int level,
5588 int pmode,
5589 FILE *f)
5590 {
5591 char b1[128], b2[128];
5592 int i, n;
5593
5594 pmode = 0; /* foil compiler warning */
5595 /* will eventually use pmode as indicated below */
5596
5597 /*
5598 * RM_PRINT_TERSE: prints the node name, and renderpass parms
5599 * (vertex dims, transparency), number of prims and prim type,
5600 * presence of scene parms, presence of render parms, etc.
5601 *
5602 * RM_PRINT_VERBOSE: prints everything.
5603 */
5604
5605 DOSPACES(level, f);
5606
5607 switch(private_rmNodeGetRenderpassOpacity(r))
5608 {
5609 case RM_RENDERPASS_OPAQUE:
5610 strcpy(b1, "RM_RENDERPASS_OPAQUE");
5611 break;
5612
5613 case RM_RENDERPASS_TRANSPARENT:
5614 strcpy(b1, "RM_RENDERPASS_TRANSPARENT");
5615 break;
5616
5617 case RM_RENDERPASS_ALL:
5618 strcpy(b1, "RM_RENDERPASS_ALL");
5619 break;
5620
5621 default:
5622 strcpy(b1, "Node Renderpass Opacity is UNDEFINED! ");
5623 break;
5624 }
5625
5626 switch(private_rmNodeGetRenderpassDims(r))
5627 {
5628 case RM_RENDERPASS_3D:
5629 strcpy(b2, "RM_RENDERPASS_3D");
5630 break;
5631
5632 case RM_RENDERPASS_2D:
5633 strcpy(b2, "RM_RENDERPASS_2D");
5634 break;
5635
5636 case RM_RENDERPASS_ALL:
5637 strcpy(b2, "RM_RENDERPASS_ALL");
5638 break;
5639
5640 default:
5641 strcpy(b2, "RenderpassDims is UNDEFINED!");
5642 break;
5643 }
5644
5645 fprintf(f, "Name: <%s> %s %s\n", private_rmNodeGetName(r), b1, b2);
5646
5647 /* print renderpassdims & renderpassopacity */
5648 n = rmNodeGetNumPrims(r);
5649 DOSPACES(level, f);
5650 fprintf(f, "Node has %d primitives\n", n);
5651
5652 for (i=0; i < n; i++)
5653 {
5654 RMprimitive *p;
5655
5656 p = r->prims[i]; /* bad! use API */
5657 private_rmPrimTypeToString(private_rmPrimitiveGetType(p), b1);
5658 DOSPACES(level, f);
5659 fprintf(f, "Prim %d type is <%s> and has %d vertices, %d colors and %d normals \n", i, b1, private_rmPrimitiveHackGetNverts(p), private_rmPrimitiveHackGetNcolors(p), private_rmPrimitiveHackGetNnormals(p));
5660 }
5661
5662 /* print scene parameters */
5663 if (r->scene_parms != NULL)
5664 {
5665 int i;
5666
5667 for (i = 0; i < RM_MAX_LIGHTS; i++)
5668 {
5669 RMlight *l;
5670
5671 l = r->scene_parms->lightSources[i];
5672 if (l != NULL)
5673 private_rmPrintLight(l, level, i, f);
5674 }
5675 {
5676 RMcamera2D *c;
5677 if (rmNodeGetSceneCamera2D(r, &c) != RM_WHACKED)
5678 {
5679 DOSPACES(level, f);
5680 fprintf(f," 2D camera present. \n");
5681 rmCamera2DDelete(c);
5682 }
5683 }
5684 {
5685 RMcamera3D *c;
5686 if (rmNodeGetSceneCamera3D(r, &c) != RM_WHACKED)
5687 {
5688 DOSPACES(level, f);
5689 fprintf(f," 3D camera present. \n");
5690 rmCamera3DDelete(c);
5691 }
5692 }
5693 {
5694 RMfog *fog;
5695 if (rmNodeGetSceneFog(r, &fog) != RM_WHACKED)
5696 {
5697 DOSPACES(level, f);
5698 fprintf(f," Fog present. \n");
5699 }
5700 }
5701 } /* end scene parms */
5702
5703 if (r->transforms != NULL)
5704 {
5705 /*
5706 * what we'll do here is just detect and report the presence of any
5707 * of the transformations. We could easily print them if needed.
5708 */
5709 DOSPACES(level, f);
5710 fprintf(f,"Transformations are present. \n");
5711
5712 } /* end transforms */
5713
5714 /* print fb clear operations */
5715 if (r->fbClear != NULL)
5716 {
5717 DOSPACES(level, f);
5718 fprintf(f, "fbClear options set: ");
5719 if (r->fbClear->bgColor != NULL)
5720 fprintf(f, "bgColor <%g %g %g %g> ", r->fbClear->bgColor->r, r->fbClear->bgColor->g, r->fbClear->bgColor->b, r->fbClear->bgColor->a);
5721 if (r->fbClear->depthValue != NULL)
5722 fprintf(f, "depthValue <%g> ", *(r->fbClear->depthValue));
5723 if (r->fbClear->bgImageTile)
5724 fprintf(f, "bgImageTile present ");
5725 if (r->fbClear->depthImage)
5726 fprintf(f, "bgDepthImage present ");
5727 fprintf(f,"\n");
5728 }
5729
5730 DOSPACES(level, f);
5731 fprintf(f, "<%s> has %d children node(s).\n", private_rmNodeGetName(r), private_rmNodeGetNumChildren(r));
5732
5733 /* print surface props */
5734 /* print render props */
5735 /* print primitive info */
5736 #if 0
5737 {
5738 DOTABS(level, f);
5739 if (r->scene_parms->viewport != NULL)
5740 {
5741 DOTABS(level, f);
5742 fprintf(f, " viewport xmin,ymin,xmax,ymax %g, %g, %g, %g \n",
5743 r->scene_parms->viewport->xmin, r->scene_parms->viewport->ymin,
5744 r->scene_parms.viewport->xmax, r->scene_parms->viewport->ymax);
5745 }
5746
5747 if (r->scene_parms->camera3d)
5748 {
5749 DOTABS(level, f);
5750 fprintf(f, " node has a 3d camera. \n");
5751 }
5752
5753 if (r->scene_parms->camera2d)
5754 {
5755 DOTABS(level, f);
5756 fprintf(f, " node has a 2d camera. \n");
5757 }
5758
5759 if (r->scene_parms.background_image_tile)
5760 {
5761 DOTABS(level, f);
5762 fprintf(f, " node has a background image tile. \n");
5763 }
5764
5765 if (r->scene_parms.background_color)
5766 {
5767 DOTABS(level, f);
5768 fprintf(f, " node has a background color. \n");
5769 }
5770 }
5771
5772 if (flags & RM_PRINT_NPRIMS)
5773 {
5774 int i;
5775
5776 fprintf(stderr, " RM_PRINT_NPRIMS code needs to be rewritten. \n")
5777
5778 /* deprecated */
5779 RMmodel *g;
5780
5781 for (i = 0; i < RM_MAX_PRIM_MODELS_PER_NODE; i++) /* this is constant... */
5782 {
5783 g = rmNodeGetModel(r, i);
5784
5785 if (g)
5786 {
5787 DOTABS(level, f);
5788 fprintf(f, "LOD %d, Nprims: %d \n", i, rmNodeGetModelNumPrims(r, i));
5789 }
5790 }
5791 }
5792 #endif
5793 }
5794
5795
5796 /* PRIVATE
5797 *
5798 * private_rmPrintSceneGraph recursively traverses a scene graph
5799 * and prints stuff. it is little used and probably incomplete
5800 */
5801 void
private_rmPrintSceneGraph(const RMnode * r,int level,RMenum pmode,FILE * f)5802 private_rmPrintSceneGraph (const RMnode *r,
5803 int level,
5804 RMenum pmode,
5805 FILE *f)
5806 {
5807 int i;
5808 RMnode *c;
5809
5810 if (pmode == RM_PRINT_VERBOSE)
5811 {
5812 rmWarning(" RM_PRINT_VERBOSE mode not quite ready yet. \n");
5813 }
5814
5815 /* print name & stuff for this node */
5816 private_rmPrintNode(r, level, pmode, f);
5817
5818 /* then print stuff for this node's children */
5819 for (i = 0; i < rmNodeGetNumChildren(r); i++)
5820 {
5821 c = rmNodeGetIthChild(r, i);
5822 private_rmPrintSceneGraph(c, (level + 1), pmode, f);
5823 }
5824 }
5825
5826 /* PRIVATE */
5827 RMenum *
private_rmEnumNew(int n)5828 private_rmEnumNew (int n)
5829 {
5830 RMenum *t;
5831
5832 t = (RMenum *)malloc(sizeof(RMenum)*n);
5833
5834 return(t);
5835 }
5836
5837
5838 /* PRIVATE */
5839 _surface_properties *
private_rmSurfacePropsNew()5840 private_rmSurfacePropsNew ()
5841 {
5842 _surface_properties *t;
5843
5844 t = (_surface_properties *)malloc(sizeof(_surface_properties));
5845 memset(t, 0, sizeof(_surface_properties));
5846
5847 return(t);
5848 }
5849
5850
5851 /* PRIVATE */
5852 _rendermode_properties *
private_rmRenderModePropsNew(void)5853 private_rmRenderModePropsNew (void)
5854 {
5855 _rendermode_properties *t;
5856
5857 t = (_rendermode_properties *)malloc(sizeof(_rendermode_properties));
5858 memset(t, 0, sizeof(_rendermode_properties));
5859
5860 return(t);
5861 }
5862
5863
5864 /* PRIVATE */
5865 void
private_initObjectTree(void)5866 private_initObjectTree (void)
5867 {
5868 float vp[4];
5869 RMvertex3D bmin, bmax;
5870
5871 root = rmNodeNew("rmlib_root", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL);
5872
5873 /* tmp, wes */
5874 rmNodeSetNormalizeNormals(root, RM_TRUE);
5875
5876 /*
5877 * the default things happen to the root node:
5878 * 1. a viewport is created which spans [0..1] in x,y (deprecated)
5879 * 2. set the bounding box to something which is uninitialized
5880 * 3. default material properties, rendering properties
5881 */
5882
5883 vp[0] = RM_DEFAULT_VIEWPORT_XMIN;
5884 vp[1] = RM_DEFAULT_VIEWPORT_YMIN;
5885 vp[2] = RM_DEFAULT_VIEWPORT_XMAX;
5886 vp[3] = RM_DEFAULT_VIEWPORT_YMAX;
5887
5888 rmNodeSetSceneViewport(root, vp);
5889
5890 bmin.x = bmin.y = bmin.z = RM_MAXFLOAT;
5891 bmax.x = bmax.y = bmax.z = RM_MINFLOAT;
5892 rmNodeSetBoundingBox(root, &bmin, &bmax);
5893
5894 /* set the default material properties */
5895 {
5896 extern float RM_DEFAULT_SPECULAR_EXPONENT;
5897 extern RMcolor4D RM_DEFAULT_AMBIENT_COLOR,RM_DEFAULT_DIFFUSE_COLOR, RM_DEFAULT_SPECULAR_COLOR, RM_DEFAULT_UNLIT_COLOR;
5898
5899 rmNodeSetAmbientColor(root, &RM_DEFAULT_AMBIENT_COLOR);
5900 rmNodeSetDiffuseColor(root, &RM_DEFAULT_DIFFUSE_COLOR);
5901 rmNodeSetSpecularColor(root, &RM_DEFAULT_SPECULAR_COLOR);
5902 rmNodeSetSpecularExponent(root, RM_DEFAULT_SPECULAR_EXPONENT);
5903 rmNodeSetUnlitColor(root, &RM_DEFAULT_UNLIT_COLOR);
5904 rmNodeSetNormalizeNormals(root, RM_FALSE);
5905
5906 /* set the default rendering parms */
5907 rmNodeSetShader(root, RM_DEFAULT_RENDERMODE); /* RM_RENDERMODE_SMOOTH */
5908
5909 rmNodeSetPolygonDrawMode(root, RM_FRONT_AND_BACK, RM_FILL);
5910
5911 rmNodeSetPolygonCullMode(root, RM_CULL_NONE);
5912 rmNodeSetFrontFace(root, RM_CCW);
5913 }
5914 rmNodeSetLineStyle(root, RM_LINES_SOLID);
5915 rmNodeSetLineWidth(root, RM_LINEWIDTH_NARROW);
5916 }
5917
5918 RMenum
private_computeCylindersBoundingBox(RMprimitive * p,RMvertex3D * returnMin,RMvertex3D * returnMax)5919 private_computeCylindersBoundingBox(RMprimitive *p,
5920 RMvertex3D *returnMin,
5921 RMvertex3D *returnMax)
5922 {
5923 /*
5924 * we assume that the bbox for a cylinder is "almost the same as"
5925 * the union of the bbox formed by two spheres of radius R at
5926 * each end of the cylinder.
5927 */
5928 int j, rstride, nradii, rveclen, vstride, nvertices, vveclen;
5929 float *radii, *vertices;
5930 RMvertex3D s1min, s1max, s2min, s2max;
5931
5932 /* get vertex and radius blob info */
5933 private_rmGetBlobData(BLOB_VERTEX_INDEX, p, &vstride, &nvertices, (void **)&vertices, &vveclen);
5934 private_rmGetBlobData(BLOB_SCALE_INDEX, p, &rstride, &nradii, (void **)&radii, &rveclen);
5935
5936 /* compute bounding boxes for all cylinders */
5937 for (j = 0; j < nvertices/2; j++, vertices += vstride, radii += rstride)
5938 {
5939 /* extents = center +/- radius */
5940 memcpy((void *)&s1min, vertices, sizeof(RMvertex3D));
5941 vertices += vstride;
5942 s1max = s1min;
5943 memcpy((void *)&s2min, vertices, sizeof(RMvertex3D));
5944 s2max = s2min;
5945
5946 s1min.x -= *radii; s1min.y -= *radii; s1min.z -= *radii;
5947 s1max.x += *radii; s1max.y += *radii; s1max.z += *radii;
5948
5949 s2min.x -= *radii; s2min.y -= *radii; s2min.z -= *radii;
5950 s2max.x += *radii; s2max.y += *radii; s2max.z += *radii;
5951
5952 /* tally sphere bounding boxes */
5953 if (j == 0)
5954 {
5955 rmUnionBoundingBoxes(&s1min, &s1max, &s2min, &s2max, returnMin, returnMax);
5956 }
5957 else
5958 {
5959 rmUnionBoundingBoxes(returnMin, returnMax, &s1min, &s1max, returnMin, returnMax);
5960 rmUnionBoundingBoxes(returnMin, returnMax, &s2min, &s2max, returnMin, returnMax);
5961 }
5962 }
5963 return(RM_CHILL);
5964 }
5965
5966
5967 /* PRIVATE: perform a boolean op on the RMnode's attribMask field */
5968 void
private_rmNodeAttribMask(RMnode * n,GLuint bitmask,RMenum op)5969 private_rmNodeAttribMask(RMnode *n,
5970 GLuint bitmask,
5971 RMenum op)
5972 {
5973 if (op == RM_OR)
5974 n->attribMask |= bitmask;
5975 else if (op == RM_AND)
5976 n->attribMask &= bitmask;
5977 else if (op == RM_SET)
5978 n->attribMask = bitmask;
5979 }
5980
5981 /* PRIVATE: return the RMnode's attribMask field */
5982 GLuint
private_rmNodeGetAttribMask(const RMnode * n)5983 private_rmNodeGetAttribMask(const RMnode *n)
5984 {
5985 return(n->attribMask);
5986 }
5987
5988 /* PRIVATE: compute an OpenGL attribute mask representing all portions of
5989 OpenGL rendering state affected by scene parms, material props, etc.
5990 contained in the input RMnode. */
5991 GLuint
private_rmNodeComputeAttribMask(const RMnode * n)5992 private_rmNodeComputeAttribMask(const RMnode *n)
5993 {
5994 /*
5995 * look at all scene parms, etc. in the node and compute an
5996 * OpenGL attribute mask
5997 */
5998 GLuint rval = 0;
5999
6000 if (n->rprops != NULL)
6001 {
6002 if ((n->rprops->normalizeNormals != NULL) &&
6003 (*(n->rprops->normalizeNormals) == RM_TRUE))
6004 rval |= GL_ENABLE_BIT;
6005
6006 if (n->rprops->shademodel != NULL)
6007 rval |= GL_LIGHTING_BIT;
6008
6009 if (n->rprops->front_face != NULL)
6010 rval |= GL_POLYGON_BIT;
6011
6012 if ((n->rprops->poly_mode_face != NULL) &&
6013 (n->rprops->poly_mode_drawstyle != NULL))
6014 rval |= GL_POLYGON_BIT;
6015
6016 if ((n->rprops->linewidth != NULL) ||
6017 (n->rprops->linestyle != NULL))
6018 rval |= GL_LINE_BIT;
6019
6020 if (n->rprops->pointsize != NULL)
6021 rval |= GL_POINT_BIT;
6022
6023 if (n->rprops->cull_mode != NULL)
6024 rval |= GL_CURRENT_BIT;
6025 }
6026
6027 if (n->sprops != NULL)
6028 {
6029 if ((n->sprops->ambient_color != NULL) ||
6030 (n->sprops->diffuse_color != NULL) ||
6031 (n->sprops->specular_color != NULL) ||
6032 (n->sprops->specular_exponent != NULL))
6033 rval |= GL_LIGHTING_BIT;
6034 }
6035
6036 if ((n->sprops != NULL) && (n->sprops->unlit_color != NULL))
6037 rval |= (GL_CURRENT_BIT | GL_LIGHTING_BIT);
6038
6039 if (n->scene_parms != NULL)
6040 {
6041 if ((n->scene_parms->cp0 != NULL) ||
6042 (n->scene_parms->cp1 != NULL) ||
6043 (n->scene_parms->cp2 != NULL) ||
6044 (n->scene_parms->cp3 != NULL) ||
6045 (n->scene_parms->cp4 != NULL) ||
6046 (n->scene_parms->cp5 != NULL))
6047 rval |= (GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT);
6048 /* rval |= (GL_ENABLE_BIT | GL_TRANSFORM_BIT); */
6049 /*
6050 * 6/5/05. For some reason, the color/lighting is also affected by
6051 * the clip planes. The clipper RMdemo revealed the problem where
6052 * the quad representing the clip plans was green when it should've
6053 * been red or blue, depending upon its orientation. Adding
6054 * the GL_LIGHTING_BIT to the above mask set when clip planes
6055 * are present fixed the problem.
6056 */
6057
6058 if (n->scene_parms->fog != NULL)
6059 rval |= GL_FOG_BIT;
6060
6061 if ((n->scene_parms->lightSources != NULL) ||
6062 (n->scene_parms->lmodel != NULL))
6063 {
6064 /* check for non-null lightSources[i] */
6065 int i;
6066 for (i=0;i<RM_MAX_LIGHTS;i++)
6067 if (n->scene_parms->lightSources[i] != NULL)
6068 break;
6069
6070 /* must have lmodel != NULL along with some non-NULL lightSource */
6071 if (i < RM_MAX_LIGHTS)
6072 rval |= GL_LIGHTING_BIT;
6073 }
6074
6075 if (n->scene_parms->haveAnyTextures == RM_TRUE)
6076 {
6077 /* rval |= GL_ALL_ATTRIB_BITS; for debug during CR development */
6078 rval |= (GL_TEXTURE_BIT | GL_ENABLE_BIT);
6079 #if 0
6080 /* intial dev code, don't need anymore, wes 2/2005 */
6081 for (i=0; i <= RM_MAX_MULTITEXTURES; i++)
6082 {
6083 if (n->scene_parms->textures[i] != NULL)
6084 {
6085 rval |= (GL_TEXTURE_BIT | GL_ENABLE_BIT);
6086 break;
6087 }
6088 }
6089 #endif
6090 }
6091
6092 if (n->scene_parms->viewport != NULL)
6093 {
6094 float *vp = n->scene_parms->viewport;
6095
6096 rval |= GL_VIEWPORT_BIT;
6097 /*
6098 * only if viewport is something other than full window will we
6099 * enable a scissor test. this same test is performed in the code
6100 * that actually sets the OpenGL parms at draw time.
6101 */
6102 if ((vp[0] != 0.0) || (vp[1] != 0.0) || (vp[2] != 1.0) || (vp[3] != 1.0))
6103 rval |= GL_SCISSOR_BIT;
6104 }
6105 }
6106
6107 if (n->fbClear != NULL)
6108 {
6109 if ((n->fbClear->depthImage != NULL) ||
6110 (n->fbClear->depthValue != NULL))
6111 rval |= GL_DEPTH_BUFFER_BIT;
6112 }
6113
6114 return(rval);
6115 }
6116
6117 /* EOF */
6118