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, &center);
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, &center);
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, &center);
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, &center);
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