/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmscene.c,v 1.15 2005/06/26 18:58:42 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.15 $
 * $Log: rmscene.c,v $
 * Revision 1.15  2005/06/26 18:58:42  wes
 * Updated rmNodeSceneGetFog to return RM_WHACKED when no RMfog is present.
 *
 * Revision 1.14  2005/06/12 21:18:23  wes
 * Minor language changes to in-code documentation.
 *
 * Revision 1.13  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.12  2005/05/16 01:02:16  wes
 * Updated code documentation.
 *
 * Revision 1.11  2005/02/24 16:17:25  wes
 * Added support for fbClears to be set at the RMpipe level. Apps can
 * still set them at the RMnode level if desired for fine-grained control.
 *
 * Revision 1.10  2005/02/19 16:22:50  wes
 * Distro sync and consolidation.
 *
 * Revision 1.9  2005/01/23 17:08:25  wes
 * Copyright updated to 2005.
 * Updates to support access and use of extensions; multitexturing on all
 * platforms, use of 3d texture extension to realize implementation of
 * volume rendering and related functions on Windows platforms.
 *
 * Revision 1.8  2004/06/22 05:01:58  wes
 * Minor tweaks to support consistent state tracking. These changes
 * were made as a result to changes in the main RMSG tree during a
 * big PS bug fixing expedition.
 *
 * Revision 1.7  2004/04/09 14:48:18  wes
 * Fix compile warnings about unused variables.
 *
 * Revision 1.6  2004/01/16 16:48:35  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.5  2003/11/22 00:53:41  wes
 * Changes to support RMtextures being shared when assigned as scene parameters
 * to properly implement instancing.
 *
 * Revision 1.4  2003/10/03 19:20:16  wes
 * Repaired bogus error message on rmNodeSetSceneLight().
 *
 * Revision 1.3  2003/02/14 00:20:51  wes
 * No significant changes.
 *
 * Revision 1.2  2003/02/02 02:07:15  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.15  2003/01/20 05:39:49  wes
 * Rewrote texture state handling code.
 *
 * Revision 1.14  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.13  2002/04/30 19:33:26  wes
 * Updated copyright dates.
 *
 * Revision 1.12  2001/10/15 00:12:25  wes
 * Different treatment of rmTextProps scene params. The rmTextProps is
 * owned by the component manager, so we can't directly memcpy stuff from
 * one to another without causing problems.
 *
 * Revision 1.11  2001/07/15 17:15:29  wes
 * Cleanup of code that manages RMtextProps objects.
 *
 * Revision 1.10  2001/05/26 14:38:19  wes
 * Doc additions.
 *
 * Revision 1.9  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.8  2000/12/05 03:02:52  wes
 * Updated rmNodeSetSceneTexture to use rmImageDup() when copying
 * images from caller-supplied Rmtexture. Code-resident docs also updated.
 *
 * Revision 1.7  2000/12/03 22:35:38  wes
 * Mods for thread safety.
 *
 * Revision 1.6  2000/08/30 02:08:55  wes
 * In rmNodeSetSceneClipPlane, moved creation of scene parms struct
 * to earlier in the routine. Its previous placement was an error.
 *
 * Revision 1.5  2000/05/17 14:20:18  wes
 * Added lots of documentation.
 *
 * Revision 1.4  2000/05/14 15:24:53  wes
 * Added lots of documentation.
 *
 * Revision 1.3  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.2  2000/02/29 23:43:53  wes
 * Compile warning cleanups.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

/* documentation of public routines is incomplete in this file. */

#include <rm/rm.h>
#include "rmprivat.h"

/* PRIVATE declarations */
internals_RMsceneParms *private_rmNodeSceneParmsNew (void);

RMenum  private_rmNodeSetSceneTexture(RMnode *n, RMtexture *t, int textureUnit);

/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneBackgroundColor
 @pstart
 RMenum rmNodeSetSceneBackgroundColor (RMnode *toModify,
			               const RMcolor4D *newColor)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (input).

 const RMcolor4D *newColor - a handle to the desired background color
   (input).
 @aend

 @dstart

 Use this routine to set the background color scene parameter.
 Calling this routine with a color argument of NULL disables the
 background color scene parameter.  During rendering, the color planes
 of the framebuffer are cleared to this background color (with a depth
 value of 1.0, see NOTE below).  

 Upon success, RM_CHILL is returned and the specified background color
 scene parameter is set.  Otherwise, RM_WHACKED is returned, and the
 background color scene parameter remains unmodified.

 Passing in a value of NULL for the RMcolor4D object will effectively
 remove any existing background image color parameter from the RMnode toModify.

 NOTE: internal to this routine, a call to rmNodeSetSceneDepthValue is
 performed, effectively coupling framebuffer and depthbuffer clears.

 Because this routine makes a copy of the input scene parameter (an
 RMcolor4D object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 Related routines: rmNodeSetSceneBackgroundImageTile, rnNodeSetSceneDepthValue.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneBackgroundColor (RMnode *r,
			       const RMcolor4D *new_color)
{
    if (RM_ASSERT(r, "rmNodeSetSceneBackgroundColor() error: the input RMnode pointer is NULL.") == RM_WHACKED)
	return(RM_WHACKED);

    if (r->fbClear == NULL)
	r->fbClear = private_rmFBClearNew();

    if (r->fbClear->bgColor != NULL)
	rmColor4DDelete(r->fbClear->bgColor);

    if (new_color != NULL)
    {
	float junk;
	
	r->fbClear->bgColor = rmColor4DNew(1);
	C4COPY(r->fbClear->bgColor, new_color);

	if (rmNodeGetSceneDepthValue(r, &junk) == RM_WHACKED)
	{
	    extern float RM_DEFAULT_DEPTH_VALUE;
	    
	    junk = RM_DEFAULT_DEPTH_VALUE;
	    rmNodeSetSceneDepthValue(r, &junk);
	}
    }
    else
	r->fbClear->bgColor = NULL;
    
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneBackgroundColor
 @pstart
 RMenum rmNodeGetSceneBackgroundColor (const RMnode *toQuery,
			               RMcolor4D *returnColor)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMcolor4D *returnColor - a handle to a caller-supplied RMcolor4D
    object (return).
 @aend

 @dstart

 Use this routine to obtain the background color scene parameter of an
 RMnode, if such a scene parameter exists.  

 Upon success, RM_CHILL is returned and the scene background color is
 copied into the caller-supplied RMcolor4D (effectively returning a copy
 of the background color scene parameter to the caller). If no such scene 
 parameter exists, or if there is some error condition detected, RM_WHACKED 
 is returned, and the caller-supplied colorReturn object remains unmodified.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneBackgroundColor (const RMnode *r,
			       RMcolor4D *color_return)
{
    if ((RM_ASSERT(r, "rmNodeGetSceneBackgroundColor() error: the input RMnode pointer is NULL.") == RM_WHACKED) ||
	(RM_ASSERT(color_return, "rmNodeGetSceneBackgroundColor() error: the return RMcolor4D pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);

    if ((r->fbClear == NULL) || (r->fbClear->bgColor == NULL))
	return(RM_WHACKED);

    /* we are returning the contents of the scene parameter, an RMcolor4D
     object, to the caller, not the handle to the RMcolor4D object stored
     in the scene graph itself. */

    *color_return = *(r->fbClear->bgColor);

    return(RM_CHILL);	
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneBackgroundImage
 @pstart
 RMenum rmNodeSetSceneBackgroundImage (RMnode *toModify,
			               const RMimage *newImageTile)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMimage *newImageTile - a handle to an RMimage (input).
 @aend

 @dstart

 Sets the background image scene parameter for the RMnode.  When
 rendered, this image is tiled into the viewport at the scene depth
 value (or a default of 1.0) with an orthogonal projection, creating a
 background image. If the image does not fit the display, it will be
 tiled from top to bottom and left to right, so ragged edges fall on
 the bottom and right edges of the viewport.

 Passing in a value of NULL for the newImageTile parameter will 
 effectively remove the background image tile scene parameter, if it
 exists.

 Upon success, RM_CHILL is returned and the background image scene
 parameter has been modified.  Otherwise, RM_WHACKED is returned.

 Because this routine makes a copy of the input scene parameter (an
 RMimage object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 NOTE: internal to this routine, a call to rmNodeSetSceneDepthValue() is
 performed, effectively coupling framebuffer clears (by image tiling) with
 depth buffer clears.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneBackgroundImage (RMnode *n,
			       const RMimage *tile)
{
    if (RM_ASSERT(n, "rmNodeSetSceneBackgroundImage() error: input RMnode is NULL. \n") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->fbClear == NULL)
	n->fbClear = private_rmFBClearNew();

    /* if there's an existing background image tile parameter, remove it. */
    if (n->fbClear->bgImageTile != NULL)
	rmImageDelete(n->fbClear->bgImageTile);

    /* if the input isn't null, make a copy and assign it as a scene
     parameter. */
    if (tile != NULL)
    {
	float junk;
	
	n->fbClear->bgImageTile =	rmImageDup(tile);

	/* if there's no background depth value, create one. this
	 policy may change in the future. */
	if (rmNodeGetSceneDepthValue(n, &junk) == RM_WHACKED)
	{
	    extern float RM_DEFAULT_DEPTH_VALUE;
	    
	    junk = RM_DEFAULT_DEPTH_VALUE;
	    rmNodeSetSceneDepthValue(n, &junk);
	}
    }
    else
	n->fbClear->bgImageTile = NULL;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneBackgroundImage
 @pstart
 RMenum rmNodeGetSceneBackgroundImage (const RMnode *toQuery,
			               RMimage **returnImageTile)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMimage **returnImageTile - a handle to an RMimage handle (return).
 @aend

 @dstart

 Use this routine to obtain the background image scene parameter of an
 RMnode.  Note that a handle to the RMimage is returned, not a
 copy of the actual pixel data.

 Upon success, RM_CHILL is returned and a handle to the scene
 background image is copied into the caller-supplied RMimage handle.
 Otherwise, RM_WHACKED is returned.

 Unlike most other rmNodeGetScene*() routines, this routine returns a
 handle to the actual object contained within the scene graph, rather
 than returning a copy. Applications should exercise appropriate
 discretion when using this object.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneBackgroundImage (const RMnode *r,
			       RMimage **tile_return)
{
    if ((RM_ASSERT(r, "rmNodeGetSceneBackgroundImage() error: input RMnode is NULL. \n") == RM_WHACKED) ||
	(RM_ASSERT(r, "rmNodeGetSceneBackgroundImage() error: input pointer to RMimage pointer is NULL. ") == RM_WHACKED))
	return(RM_WHACKED);

    /* detect the absence of any scene parms, or the absence of the
       background image tile scene parm.  */

    if ((r->fbClear == NULL) || (r->fbClear->bgImageTile == NULL))
	return(RM_WHACKED);
	
    *tile_return = r->fbClear->bgImageTile;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneCamera2D
 @pstart
 RMenum rmNodeSetSceneCamera2D (RMnode *toModify,
		                const RMcamera2D *newCamera)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMcamera2D *newCamera - a handle to an RMcamera2D object (input).
 @aend

 @dstart

 Assign a 2D camera as a scene parameter to an RMnode. The 2D camera scene
 parameter defines a rectangular viewing region, and results in the
 render-time modification of the OpenGL view and projection matrices.

 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned and the RMnode's scene parameters remain unmodified.

 Passing in a value of NULL for the RMcamera2D object will effectively
 remove any existing 2D camera scene parameter from the RMnode toModify.

 Because this routine makes a copy of the input scene parameter (an
 RMcamera2D object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneCamera2D (RMnode *n,
		        const RMcamera2D *c)
{
    if (RM_ASSERT(n, "rmNodeSetSceneCamera2D() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    if (n->scene_parms->camera2d != NULL)
    {
	rmCamera2DDelete(n->scene_parms->camera2d);
	n->scene_parms->camera2d = NULL;
    }

    if (c != NULL)
    {
	n->scene_parms->camera2d = rmCamera2DNew();
	*(n->scene_parms->camera2d) = *c;
    }
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneCamera2D
 @pstart
 RMenum rmNodeGetSceneCamera2D (const RMnode *toQuery,
		                RMcamera2D **returnCamera)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMcamera2D **returnCamera - a handle to anRMcamera2D object (return). 
 @aend

 @dstart

 If a 2D camera is defined for the RMnode, then a copy of the 2D
 camera is returned in the caller-supplied handle, and RM_CHILL is
 returned. Otherwise, RM_WHACKED is returned, and the caller supplied
 RMcamera2D object handle remains unmodified.

 Because this routine returns a COPY of the RMnode's scene parameter (an
 RMcamera2D object), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmCamera2DDelete the object when it is no longer needed).
 

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneCamera2D (const RMnode *n,
		        RMcamera2D **c)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneCamera2D() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(c, "rmNodeGetSceneCamera2D() error: the input pointer to an RMcamera2D pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);
    
    if ((n->scene_parms == NULL) || (n->scene_parms->camera2d == NULL))
	return(RM_WHACKED);

    *c = rmCamera2DNew();
    *(*c) = *(n->scene_parms->camera2d);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneCamera3D
 @pstart
 RMenum rmNodeSetSceneCamera3D (RMnode *toModify,
		                const RMcamera3D *newCamera)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMcamera3D *newCamera - a handle to an RMcamera3D (input).
 @aend

 @dstart

 Assign a 3D camera as a scene parameter to an RMnode. The 3D camera
 defines a viewing volume in world coordinats. Objects that lie within
 this view volume will be visible when rendered. The 3D camera scene
 parameter will produce a render-time modification of the OpenGL
 modelview and projection matrices. The effect of these matrix stack
 modifications have scope only over the subtree rooted at the RMnode
 toModify.

 Passing in a value of NULL for the input RMcamera3D object will effectively
 remove any existing RMcamera3D scene parameter from the RMnode.

 Upon success, a copy of the input RMcamera3D object is made (if not NULL), 
 and assigned as a scene parameter to the input RMnode; Otherwise, RM_WHACKED 
 is returned.

 Because this routine makes a copy of the input scene parameter (an
 RMcamera3D object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneCamera3D (RMnode *n,
		        const RMcamera3D *c)
{
    if (RM_ASSERT(n, "rmNodeSetSceneCamera3D() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    if (n->scene_parms->camera3d != NULL)
    {
	rmCamera3DDelete(n->scene_parms->camera3d);
	n->scene_parms->camera3d = NULL;
    }

    if (c != NULL)
    {
	n->scene_parms->camera3d = rmCamera3DNew();
	*(n->scene_parms->camera3d) = *c;
    }
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneCamera3D
 @pstart
 RMenum rmNodeGetSceneCamera3D (const RMnode *toQuery,
		                RMcamera3D **returnCamera)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMcamera3D **returnCamera - a handle to a caller-supplied RMcamera3D object
    (modified).
 @aend

 @dstart

 If a 3D camera is defined for the RMnode, then a copy of the 3D
 camera is created, then returned in the caller-supplied handle, and 
 RM_CHILL is returned. Otherwise, RM_WHACKED is returned and the 
 caller-supplied RMcamera3D handle remains unmodified.

 Because this routine returns a COPY of the RMnode's scene parameter (an
 RMcamera3D object), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmCamera3DDelete the object when it is no longer needed).
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneCamera3D (const RMnode *n,
		        RMcamera3D **c)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneCamera3D() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(c, "rmNodeGetSceneCamera3D() error: the input pointer to an RMcamera3D pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);
    
    if ((n->scene_parms == NULL) || (n->scene_parms->camera3d == NULL))
	return(RM_WHACKED);

    *c = rmCamera3DNew();
    *(*c) = *(n->scene_parms->camera3d);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneClipPlane
 @pstart
 RMenum rmNodeSetSceneClipPlane (RMnode *toModify,
			         RMenum clipPlaneEnum,
				 const RMclipPlane *newClipPlane)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (input).

 RMenum clipPlaneEnum - an RMenum specifying which clipping plane
    to modify.  Must be one of [RM_SCENE_CLIP_PLANE0..5] (input).

 const RMclipPlane *newClipPlane - a handle to an RMclipPlane handle (input).
 @aend

 @dstart

 Assigns a clip plane as a scene parameter to an RMnode. This routine will
 make a copy of the input RMclipPlane object, and assign the copy as a
 scene parameter to the input RMnode. See rmClipPlaneNew() for more
 details about how clip planes are specified.

 Passing in a value of NULL for the newClipPlane parameter will 
 effectively remove the clip plane scene parameter specified by
 clipPlaneEnum, if it exists.

 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned.

 Because this routine makes a copy of the input scene parameter (an
 RMclipPlane), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneClipPlane (RMnode *n,
			 RMenum which_clip_plane,
			 const RMclipPlane *ncp)
{
    RMclipPlane **target = NULL, *t;
    
    if (RM_ASSERT(n, "rmNodeSetSceneClipPlane() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    switch (which_clip_plane)
       {
       case RM_SCENE_CLIP_PLANE0:
	  target = &(n->scene_parms->cp0);
	  break;

       case RM_SCENE_CLIP_PLANE1:
	  target = &(n->scene_parms->cp1);
	  break;

       case RM_SCENE_CLIP_PLANE2:
	  target = &(n->scene_parms->cp2);
	  break;

       case RM_SCENE_CLIP_PLANE3:
	  target = &(n->scene_parms->cp3);
	  break;

       case RM_SCENE_CLIP_PLANE4:
	  target = &(n->scene_parms->cp4);
	  break;

       case RM_SCENE_CLIP_PLANE5:
	  target = &(n->scene_parms->cp5);
	  break;

       default:
	  rmWarning("rmNodeSetSceneClipPlane error: bad clip plane enumerator specified by calling routine. ");
	  return(RM_WHACKED);
       }

    /* this conditional should never evaluate to TRUE. */
    if (RM_ASSERT(target,"rmNodeSetSceneClipPlane() error: input enumerator specifying a clip plane is invalid.") == RM_WHACKED)
	return(RM_WHACKED);

    t = *target;

    if (t != NULL)
    {
	rmClipPlaneDelete(t);
	*target = NULL;
    }
    
    if (ncp != NULL)
    {
	t = *target = rmClipPlaneNew();
	memcpy((void *)t, (void *)ncp, sizeof(RMclipPlane));
    }

    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneClipPlane
 @pstart
 RMenum rmNodeGetSceneClipPlane (const RMnode *toQuery,
			         RMenum clipPlaneEnum,
				 RMclipPlane **returnClipPlane)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMenum clipPlaneEnum - an RMenum indicating which clip plane is
    being modified.  Must be one of RM_SCENE_CLIP_PLANE[0-5] (input).

 RMclipPlane **returnClipPlane - a caller-supplied handle to an RMclipPlane 
    handle (return).
 @aend

 @dstart

 Use this routine to obtain one of six possible clip planes from an
 RMnode's scene parameters. 

 Upon success, a copy of the requested clip plane is created and returned to
 the caller via the returnClipPlane handle, and RM_CHILL is returned.
 Otherwise, RM_WHACKED is returned, and the caller supplied RMclipPlane
 handle remains unmodified.

 Because this routine returns a COPY of the RMnode's scene parameter (an
 RMclipPlane object), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmClipPlaneDelete the object when it is no longer needed).
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneClipPlane (const RMnode *n,
			 RMenum which_clip_plane,
			 RMclipPlane **rcp)
{
    RMclipPlane **target=NULL, *t;
    
    if ((RM_ASSERT(n, "rmNodeGetSceneClipPlane() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(rcp, "rmNodeGetSceneClipPlane() error: the input pointer to an RMclipPlane pointer is NULL. ") == RM_WHACKED))
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	return(RM_WHACKED);

    switch (which_clip_plane)
       {
       case RM_SCENE_CLIP_PLANE0:
	  target = &(n->scene_parms->cp0);
	  break;

       case RM_SCENE_CLIP_PLANE1:
	  target = &(n->scene_parms->cp1);
	  break;

       case RM_SCENE_CLIP_PLANE2:
	  target = &(n->scene_parms->cp2);
	  break;

       case RM_SCENE_CLIP_PLANE3:
	  target = &(n->scene_parms->cp3);
	  break;

       case RM_SCENE_CLIP_PLANE4:
	  target = &(n->scene_parms->cp4);
	  break;

       case RM_SCENE_CLIP_PLANE5:
	  target = &(n->scene_parms->cp5);
	  break;
	  
       default: /* bogus clip plane enum */
	  rmWarning("rmNodeGetSceneClipPlane() error: bad clip plane enumerator specified by calling routine.");
	  return(RM_WHACKED);
       }

    /* this conditional should NEVER evaluate to true. */
    if (RM_ASSERT(target,"rmNodeGetSceneClipPlane() error: input enumerator specifying a clipping plane is invalid.") == RM_WHACKED)
	return(RM_WHACKED);

    t = rmClipPlaneNew();
    memcpy((void *)t, (void *)(*target), sizeof(RMclipPlane));
    *rcp = t;	/* the app is now responsible for this memory */
    
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneDepthImage
 @pstart
 RMenum rmNodeSetSceneDepthImage (RMnode *toModify,
			          const RMimage *newDepthImage)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMimage *newDepthImage - a handle to a depth image (input)
 @aend

 @dstart

 Assigns a depth image as a scene parameter for the RMnode. The 
 depth image is similar to the background image tile, but is applied to the
 depth buffer, rather than the color planes of the framebuffer. Like the 
 background image tile, the depth image scene parameter is tiled to fill
 the framebuffer, starting from the upper left-hand corner of the viewport. 
 If the size of the depth buffer image and viewport do not match exactly,
 the "ragged edges" or on the right and the bottom of the viewport.

 Passing in a value of NULL for the RMimage object will have the effect
 of removing the background depth image scene parameter, if one exists,
 from the RMnode.

 Upon success, a copy of the caller's RMimage object is made, and the
 copy is assigned as a scene parameter (or, if the input RMimage object is
 NULL, the NULL is assigned as a depth image scene parameter, effectively
 turning off that scene parameter), and RM_CHILL is returned; 
 otherwise, RM_WHACKED is returned.

 Because this routine makes a copy of the input scene parameter (an
 RMimage), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 Note: as a practical matter, it is suggested that the pixel format
 of input RMimage objects should be of type RM_FLOAT, and in the
 range 0..1. By default, depth buffer Pixels in OpenGL range from
 0..1 (or perhaps 0..0.9999, depending upon your interpretation), but
 this range may be manipulated with glPixelTransferf(). As of the time
 of this writing (May 2000), we have tested background depth images
 only with RMimage's consisting of RM_FLOAT pixels in the range 0..1
 (and they work - refer to the demonstration program "pdb" included with
 the openrm-demo distribution).

 @dend
 * ----------------------------------------------------
 */
RMenum

rmNodeSetSceneDepthImage (RMnode *n,
			  const RMimage *new_depth_image)
{
    if (RM_ASSERT(n, "rmNodeSetSceneDepthImage() error: the input RMnode pointer is NULL.") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->fbClear == NULL)
	n->fbClear = private_rmFBClearNew();

    if (n->fbClear->depthImage != NULL)
    {
	rmImageDelete(n->fbClear->depthImage);
	n->fbClear->depthImage = NULL;
    }

    if (new_depth_image != NULL)
	n->fbClear->depthImage = rmImageDup(new_depth_image);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneDepthImage
 @pstart
 RMenum rmNodeGetSceneDepthImage (const RMnode *toQuery,
			          RMimage **returnDepthImage)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMimage **returnDepthImage - a handle to a depth image handle
    (return). 
 @aend

 @dstart

 Use this routine to obtain the RMnode's depth image scene parameter. If
 such a scene parameter exists, the handle of the RMimage (depth image)
 scene parameter is copied into caller-supplied memory, and RM_CHILL is
 returned. Otherwise, in the event of an error or the absence of the
 depth image scene parameter, RM_WHACKED is returned and caller supplied
 memory remains unmodified.

 Unlike most other rmNodeGetScene*() routines, this routine returns a
 handle to the actual RMimage object contained within the scene graph, rather
 than returning a copy. Applications should exercise appropriate
 discretion when using this object.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneDepthImage (const RMnode *n,
			  RMimage **depth_image_return)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneDepthImage() error: input RMnode is NULL. \n") == RM_WHACKED) ||
	(RM_ASSERT(depth_image_return, "rmNodeGetSceneDepthImage() error: input pointer to RMimage pointer is NULL. ") == RM_WHACKED))
	return(RM_WHACKED);

    if ((n->fbClear == NULL) || (n->fbClear->depthImage == NULL))
	return(RM_WHACKED);
	
    *depth_image_return = n->fbClear->depthImage;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneDepthValue
 @pstart
 RMenum rmNodeSetSceneDepthValue (RMnode *toModify,
			          const float *newDepthValue)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const float *newDepthValue - a handle to the new depth value for the
    node (input).
 @aend

 @dstart

 Use this routine to set the scene depth value parameter for an
 RMnode. The presence of the scene depth value has the effect of
 clearing the depth buffer to the value of newDepthValue.

 Passing in a value of NULL for newDepthValue has the effect of disabling
 depth buffer clears.

 The input newDepthValue should have the magnitude specified by
 glPixelTransferf(GL_DEPTH_SCALE,X) and range specified by
 glPixelTransferf(GL_DEPTH_BIAS,X). In OpenGL, the default depth
 bias is 0.0 and range/scale is 1.0.

 Upon success, RM_CHILL is returned and the node's depth value is set.
 Otherwise, RM_WHACKED is returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneDepthValue (RMnode *n,
			  const float *newDepthValue)
{
    if (RM_ASSERT(n, "rmNodeSetSceneDepthValue() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    if (n->fbClear == NULL)
	n->fbClear = private_rmFBClearNew();

    if (n->fbClear->depthValue != NULL)
    {
	free((void *)(n->fbClear->depthValue));
	n->fbClear->depthValue = NULL;
    }
    if (newDepthValue != NULL)
    {
	n->fbClear->depthValue = rmFloatNew(1);
	*(n->fbClear->depthValue) = *newDepthValue;
    }
	
    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneDepthValue
 @pstart
 RMenum rmNodeGetSceneDepthValue (const RMnode *toQuery,
   			          float *returnDepthValue)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 float *returnDepthValue - a handle to a caller-supplied float for the 
    queried depth value (return). 
 @aend

 @dstart 

 Use this routine to query the scene depth value for a given RMnode.

 Upon success, RM_CHILL is returned and the node's depth value is
 copied into the caller-supplied float.  If the specified pointers are
 NULL, or no valid scene depth parameter exists, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneDepthValue (const RMnode *n,
			  float *dr)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneDepthValue() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(dr, "rmNodeGetSceneDepthValue() error: the return float pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);
    
    if ((n->fbClear == NULL) || (n->fbClear->depthValue == NULL))
	return(RM_WHACKED);

    *dr = *(n->fbClear->depthValue);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneFog
 @pstart
 RMenum rmNodeSetSceneFog (RMnode *toModify,
		           const RMfog *newFog)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMfog *newFog - a handle to the new fog scene parameters
    (input).
 @aend

 @dstart

 Use this routine to set the fog scene parameters for an RMnode.  If
 no scene parameters have been defined, then the RMfog object is
 duplicated into the RMnode.  Existing fog parameters are first deleted,
 thenn replaced with the new fog parameters.  Calling this routine with a
 an argument of NULL for the RMfog object disables the fog scene parameters.

 When a non-NULL RMfog object is added as a scene parameter to an
 RMnode, fogging will become enabled. The fogging will have scope only
 over the subtree rooted at the RMnode toModify. When the RMfog object
 is NULL, fogging is disabled (unless fogging is enabled at an
 anscestor node in the scene graph).

 Upon success, RM_CHILL is returned and the node's fog parameters are
 set.  Otherwise, RM_WHACKED is returned.

 Because this routine makes a copy of the input scene parameter (an
 RMfog object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneFog (RMnode *n,
		   const RMfog *newFog)
{
    if (RM_ASSERT(n, "rmNodeSetSceneFog() error: the input RMnode pointer is NULL.") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    if (n->scene_parms->fog != NULL)
    {
	rmFogDelete(n->scene_parms->fog);
	n->scene_parms->fog = NULL;
    }

    if (newFog != NULL)
	n->scene_parms->fog = rmFogDup(newFog);

    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    return(RM_CHILL);
}

/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneFog
 @pstart
 RMenum rmNodeGetSceneFog (const RMnode *toQuery,
	 	           RMfog **returnFog)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMfog **returnFog - a pointer to an RMfog handle (return).
 @aend

 @dstart

 Use this routine to query the fog scene parameters for an RMnode.  If
 no fog scene parameters exists for the RMnode, then RM_WHACKED
 is returned and no changes will be made to the input returnFog
 parameter.

 Upon success, RM_CHILL is returned and a handle to the node's fog
 scene parameters are copied into the caller-supplied handle.

 Unlike most other rmNodeGetScene*() routines, this routine returns a
 handle to the actual RMfog object contained within the scene graph, rather
 than returning a copy. Applications should exercise appropriate
 discretion when using this object.
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneFog (const RMnode *n,
		   RMfog **returnFog)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneFog() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(returnFog, "rmNodeGetSceneFog() error: the returnFog pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);

    if (n->scene_parms == NULL || n->scene_parms->fog == NULL)
	return RM_WHACKED;

    *returnFog = n->scene_parms->fog;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneLight
 @pstart
 RMenum rmNodeSetSceneLight (RMnode *toModify,
		             RMenum whichLightEnum,
			     const RMlight *newLight)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 RMenum whichLightEnum - an RMenum specifying which light tp modify.
    Must be one of [RM_LIGHT0..7] (input).

 const RMlight *newLight - a handle to an RMlight handle (input).
 @aend

 @dstart

 Use this routine to assign a light source as a scene parameter to
 an RMnode. The light source is specified with an RMlight object
 (see rmLightNew for more information). Up to eight light sources
 may be simultaneously active, or specified at an RMnode by the
 RMenum values RM_LIGHT0, RM_LIGHT1, ... , RM_LIGHT7.

 When a non-NULL light source is assigned as a scene parameter, that
 light source becomes active, and has scope over the subtree rooted
 at the RMnode toModify within the scene graph. Assinging a NULL
 light source to one of the eight potential light source scene
 parameters will deactivate that particular light source.
 
 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned.

 Because this routine makes a copy of the input scene parameter (an
 RMlight object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneLight (RMnode *n,
		     RMenum which_light,
		     const RMlight *l)
{
    RMlight **target=NULL, *t;
    
    if (RM_ASSERT(n, "rmNodeSetSceneLight() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    switch (which_light)
       {
       case RM_LIGHT0:
	  target = &(n->scene_parms->lightSources[0]);
	  break;
	  
       case RM_LIGHT1:
	  target = &(n->scene_parms->lightSources[1]);
	  break;
	  
       case RM_LIGHT2:
	  target = &(n->scene_parms->lightSources[2]);
	  break;
	  
       case RM_LIGHT3:
	  target = &(n->scene_parms->lightSources[3]);
	  break;
	  
       case RM_LIGHT4:
	  target = &(n->scene_parms->lightSources[4]);
	  break;
	  
       case RM_LIGHT5:
	  target = &(n->scene_parms->lightSources[5]);
	  break;
	  
       case RM_LIGHT6:
	  target = &(n->scene_parms->lightSources[6]);
	  break;
	  
       case RM_LIGHT7:
	  target = &(n->scene_parms->lightSources[7]);
	  break;
	  
       default: /* bogus light enum */
	  rmWarning("rmNodeSetSceneLight() error: bad light enumerator specified by calling routine.");
	  return(RM_WHACKED);
       }

    if (RM_ASSERT(target, "rmNodeSetSceneLight() error: input enumerator specifying a light is invalid.") == RM_WHACKED)
	return(RM_WHACKED);

    t = *target;

    if (t != NULL)
    {
	rmLightDelete(t);
	*target = NULL;
    }
    
    if (l != NULL)
    {
	t = *target = rmLightNew();
	memcpy((void *)t, (void *)l, sizeof(RMlight));
    }
	
    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    return(RM_CHILL);
}

/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneLight
 @pstart
 RMenum rmNodeGetSceneLight (const RMnode *toQuery,
		             RMenum whichLightEnum,
			     RMlight **returnLightCopy)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMenum whichLightEnum - an RMenum indicating which RMlight to query.  
    Must be one of RM_LIGHT0, RM_LIGHT1, ... , RM_LIGHT7 (input).

 RMlight **returnLightCopy - a caller-supplied handle to an RMlight handle
    (return).
 @aend

 @dstart

 Use this routine to obtain one of eight possible lights defined as
 scene parameters for an RMnode.  If no such scene parameter is
 defined, RM_WHACKED is returned, and the caller-supplied pointer to
 an RMlight pointer remains unmodified. 

 Upon success, RM_CHILL is returned, and a copy of the specified
 RMlight object is created, and the handle to the new copy is
 returned via the caller-supplied handle. Applications 

 Because this routine returns a COPY of the RMnode's scene parameter (an
 RMlight object), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmLightDelete the object when it is no longer needed).
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneLight (const RMnode *n,
		     RMenum which_light,
		     RMlight **l)
{
    RMlight **target=NULL,*t;
    
    if ((RM_ASSERT(n, "rmNodeGetSceneLight() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(l, "rmNodeGetSceneLight() error: the input pointer to an RMlight pointer is NULL. ") == RM_WHACKED))
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	return(RM_WHACKED);

    switch (which_light)
       {
       case RM_LIGHT0:
	  target = &(n->scene_parms->lightSources[0]);
	  break;
	  
       case RM_LIGHT1:
	  target = &(n->scene_parms->lightSources[1]);
	  break;
	  
       case RM_LIGHT2:
	  target = &(n->scene_parms->lightSources[2]);
	  break;
	  
       case RM_LIGHT3:
	  target = &(n->scene_parms->lightSources[3]);
	  break;
	  
       case RM_LIGHT4:
	  target = &(n->scene_parms->lightSources[4]);
	  break;
	  
       case RM_LIGHT5:
	  target = &(n->scene_parms->lightSources[5]);
	  break;
	  
       case RM_LIGHT6:
	  target = &(n->scene_parms->lightSources[6]);
	  break;
	  
       case RM_LIGHT7:
	  target = &(n->scene_parms->lightSources[7]);
	  break;
	  
       default: /* bogus light enum */
	  rmWarning("rmNodeGetSceneLight() error: bad light enumerator specified by calling routine.");
	  return(RM_WHACKED);
       }

    if (RM_ASSERT(*target, "rmNodeGetSceneLight() error: the input node has no light source associated with the input RM_LIGHT* enumerator.") == RM_WHACKED)
	return(RM_WHACKED);

    t = rmLightNew();
    memcpy((void *)t, (void *)(*target), sizeof(RMlight));
    *l = t; /* the app will now be responsible for this memory */
    
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneLightModel
 @pstart
 RMenum rmNodeSetSceneLightModel (RMnode *toModify,
			          const RMlightModel *newLightModel)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMlightModel *newLightModel - a handle to an RMlightModel (input).
 @aend

 @dstart

 Set the light model used by an RMnode.  The RMlightmodel controls the
 ambient light color, whether two-sided lighting is enabled, and
 whether the local viewer is enabled for specular calculations.  If no
 light model has been defined, then the RMlightModel object is
 duplicated into the RMnode.  An existing light model is deleted and
 overwritten with the new light model.

 Calling this routine with an RMlightModel argument of NULL has the
 effect of removing any existing light model specification from the
 input RMnode. A light model that is specified at an ancestor
 node in the scene graph, if any, will be used to control light
 model parameters.

 See rmLightModelNew for details of the RMlightModel parameters.

 Upon success, RM_CHILL is returned and the RMnode contains the new
 light model.  Otherwise, RM_WHACKED is returned.

 Because this routine makes a copy of the input scene parameter (an
 RMlightModel object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneLightModel (RMnode *n,
			  const RMlightModel *lm)
{
    if (RM_ASSERT(n, "rmNodeSetSceneLightModel() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    if (n->scene_parms->lmodel != NULL)
    {
	rmLightModelDelete(n->scene_parms->lmodel);
	n->scene_parms->lmodel = NULL;
    }

    if (lm != NULL)
    {
	n->scene_parms->lmodel = rmLightModelNew();
	memcpy((void *)(n->scene_parms->lmodel), (void *)lm, sizeof(RMlightModel));
    }
    
    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneLightModel
 @pstart
 RMenum rmNodeGetSceneLightModel (const RMnode *toQuery,
			          RMlightModel **lightModelReturn)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMlightModel **returnLightModel - a caller-supplied handle to an RMlightModel
    handle (return).
 @aend

 @dstart

 Obtains a copy of the light model scene parameter for an RMnode. If no
 such scene parameter exists, RM_WHACKED is returned, and the pointer to the
 caller-supplied RMlightModel pointer remains unmodified.

 Otherwise, a copy of the RMnode's RMlightModel parameter is created,
 the pointer to this new object is copied into caller-supplied memory,
 and RM_CHILL is returned.

 Because this routine returns a COPY of the RMnode's scene parameter (an
 RMlightModel object), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmLightModelDelete the object when it is no longer needed).

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneLightModel (const RMnode *n,
			  RMlightModel **lm)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneLightModel() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(lm, "rmNodeGetSceneLightModel() error: the input pointer to an RMlightModel  pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);
    
    if ((n->scene_parms == NULL) ||
	(n->scene_parms->lmodel == NULL))
	return(RM_WHACKED);

    *lm = rmLightModelNew();
    memcpy((void *)(*lm), (void *)(n->scene_parms->lmodel), sizeof(RMlightModel));

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmTextPropsNew
 @pstart
 RMtextProps * rmTextPropsNew (void)
 @pend

 @astart
 No arguments.
 @aend

 @dstart

 Creates a new RMtextProps object and returns a handle to the caller.

 Upon success, the returned handle points to a new RMtextProps object.
 If resources cannot be allocated, then a NULL handle is returned.

 The RMtextProps object may be assigned as a scene parameter to RMnodes,
 where it will control text rendering properties, such as font family,
 size, italizication, etc. These text properties may be thought of in
 the same way as other drawing parameters, such as material properties,
 current color, and so forth.

 The defaults text rendering properties set by rmTextPropsNew() are
 controlled by definitions in rmglobals.c, and are:

 <pre>
RMenum RM_DEFAULT_HJUSTIFY    = RM_LEFT;
RMenum RM_DEFAULT_VJUSTIFY    = RM_BOTTOM;
RMenum RM_DEFAULT_FONT_FAMILY = RM_FONT_SANS;
RMenum RM_DEFAULT_FONT_SIZE   = RM_FONT_M;
RMenum RM_DEFAULT_FONT_BOLD   = RM_FALSE;
RMenum RM_DEFAULT_FONT_ITALIC = RM_FALSE;
</pre>

 Use rmTextPropsSetAttribs() to set the attributes of an RMtextProps
 object, or rmTextPropsGetAttribs() to query attributes.
 
 @dend
 * ----------------------------------------------------
 */
RMtextProps *
rmTextPropsNew (void)
{
    /* these are set in rmglobals.c */
    extern int RM_DEFAULT_FONT_FAMILY, RM_DEFAULT_FONT_SIZE;
    extern RMenum RM_DEFAULT_FONT_BOLD, RM_DEFAULT_FONT_ITALIC, 
	RM_DEFAULT_HJUSTIFY, RM_DEFAULT_VJUSTIFY;
    
    RMtextProps *t;

    t = private_rmTextPropsNew();
    
    if (RM_ASSERT(t, "rmTextPropsNew() error: malloc failure. ") == RM_WHACKED)
	return(NULL);

    rmTextPropsSetAttribs(t,
			  RM_DEFAULT_FONT_FAMILY,
			  RM_DEFAULT_FONT_SIZE,
			  RM_DEFAULT_FONT_BOLD,
			  RM_DEFAULT_FONT_ITALIC,
			  RM_DEFAULT_HJUSTIFY,
			  RM_DEFAULT_VJUSTIFY);
    return(t);
}

/*
 * ----------------------------------------------------
 * @Name rmTextPropsDelete
 @pstart
 RMenum rmTextPropsDelete (RMtextProps *toDelet)
 @pend

 @astart
 RMtextProps *toDelete - a handle to an RMtextProps object (deleted).
 @aend

 @dstart

 Deletes the resources associated with an RMtextProps object.

 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmTextPropsDelete (RMtextProps *t)
{
    if (RM_ASSERT(t, "rmTextPropsDelete() error: the input RMtextProps object is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    return(private_rmTextPropsDelete(t));
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneTextProps
 @pstart
 RMenum rmNodeSetSceneTextProps (RMnode *toModify,
			         const RMtextProps *newTextProps)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const RMtextProps *newTextProps - a handle to an RMtextProps object (input).
 @aend

 @dstart

 Assigns an RMtextProps object as a scene parameter for an RMnode. These
 text properties will control how strings are rendered within the
 subtree rooted at the RMnode toModify, unless subsequently overridden
 at a descendent RMnode. The text properties specify a font family,
 point size, horizontal and vertical justification control,  and
 emboldening and italicization parameters.

 If the input RMtextProps object is NULL, any existing RMtextProps
 scene parameter is deleted.

 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned.

 Because this routine makes a copy of the input scene parameter (an
 RMtextProps object), callers do not need to manage the input object after a
 successful return from this routine. This has important ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneTextProps (RMnode *n,
			 const RMtextProps *t)
{
    if (RM_ASSERT(n, "rmNodeSetSceneTextProps() error: the input RMnode is NULL. \n") == RM_WHACKED)
	return(RM_WHACKED);

    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    if ((n->scene_parms->textProps == NULL) && (t != NULL))
	n->scene_parms->textProps = rmTextPropsNew();

    if (t != NULL)
	private_rmTextPropsCopy(t, n->scene_parms->textProps);
    else
    {
	if (n->scene_parms->textProps != NULL)
	    rmTextPropsDelete(n->scene_parms->textProps);
	t = NULL;
    }

#if 0
    /* this code would actually generate OpenGL display lists for
     the string using the requested font and style. we're going to
     delay doing this until render time. */
    /* register the font, size, embolden, italic with RM's font registry */
    if (t != NULL)
	private_rmPrepareBitmapFont(n->scene_parms->textProps);
#endif

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneTextProps
 @pstart
 RMenum rmNodeGetSceneTextProps (const RMnode *toQuery,
                                 RMtextProps **returnTextProps)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMtextProps **returnTextProps - a caller-supplied handle to an
   RMtextProps handle (return).
 @aend

 @dstart

 Obtains a copy of the RMtextProps object scene parameter for the
 RMnode, and returns a handle to the copy in the parameter returnTextProps,
 and RM_CHILL is returned on the stack.
 
 If no such scene parameter exists, or if there is some other error
 condition encountered, RM_WHACKED is returned on the stack, and
 the caller-supplied parameter returnTextProps remains unmodified.

 Because this routine returns a COPY of the RMnode's scene parameter (an
 RMtextProps object), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmTextProps the object when it is no longer needed).
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneTextProps (const RMnode *n,
			 RMtextProps **t)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneTextProps() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(t, "rmNodeGetSceneTextProps() error: the input pointer to an RMtextProps  pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);
    
    if ((n->scene_parms == NULL) || (n->scene_parms->textProps == NULL))
	return(RM_WHACKED);

    *t = rmTextPropsNew();
    private_rmTextPropsCopy(n->scene_parms->textProps, *t);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneTexture
 @pstart
 RMenum rmNodeSetSceneTexture (RMnode *toModify,
		               const RMtexture *newTexture)
 @pend

 @astart
 RMnode *n - a handle to an RMnode (modified).

 RMtexture *t - a handle to an RMtexture (input).
 @aend

 @dstart

 Assigns an RMtexture object as a scene parameter to the RMnode.
 The presence of a texture scene parameter will cause texture mapping
 to become active at the RMnode toModify, and will remain active
 using the supplied texture newTexture over the entire subtree
 rooted at the RMnode toModify, unless overridden by a different
 texture at a descendent node within the scene graph.

 In order for texture mapping to occur at the pixel fragment level,
 primitives need to have texture coordinates (as well as vertex data).

 Specifying a value of NULL for the input texture will remove
 any existing texture scene parameter from the RMnode, effectively
 disabling texture mapping within the scene graph subtree rooted
 at toModify.
 
 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned.

 Starting with OpenRM version 1.5.1 and later, when you assign an
 RMtexture as a scene parameter to an RMnode, only a reference to the
 source RMtexture is copied. In this way, you can use a single RMtexture
 object as a source texture for an arbitrary number of scene graph nodes
 without incurring additional memory storage overhead for each instance
 of the texture. The caveat from an application developer's perspective
 is that you must treat RMtextures like RMnodes - don't delete them
 until they are really no longer needed.

 If you try to rmTextureDelete a texture that has been assigned as a
 scene parameter, rmTextureDelete will fail - the RMtexture has a
 reference counter that is incremented or decremented when it is assigned
 or removed as a scene parameter at the scene graph node level. The
 simplest way to delete the RMtexture assigned to one or more nodes
 as a scene parameter is to simply delete the node or tree of nodes.
 All associated scene parameters will be deleted when their reference
 counters reach a value of zero.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneTexture (RMnode *n,
		       RMtexture *t)
{
    RMenum rstat;

    if (RM_ASSERT(n, "rmNodeSetSceneTexture() error: the input RMnode pointer is NULL.") == RM_WHACKED)
	return RM_WHACKED;
    
    /* the index RM_MAX_MULTITEXTURES is reserved for "plain old,
     non-multitextured textures */
    rstat = private_rmNodeSetSceneTexture(n, t, RM_MAX_MULTITEXTURES);

    return rstat;
}

/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneMultiTexture
 @pstart
 RMenum rmNodeSetSceneMultiTexture (RMnode *toModify,
		                    const RMtexture *newTexture,
				    int textureUnit)
 @pend

 @astart
 RMnode *n - a handle to an RMnode (modified).

 RMtexture *t - a handle to an RMtexture (input).

 int textureUnit - an integer value (input).
 @aend

 @dstart

 Similar in function to rmNodeSetSceneTexture, but different in the 
 respect that the RMtexture is assigned to a specific multitexturing
 texturing unit.

 Multitexturing the process whereby pixel fragments are colored using 
 texel colors obtained from multiple textures using multiple texture
 coordinates. It is not supported on all OpenGL platforms. During the
 rmPipeMakeCurrent() initialization sequence, the underlying OpenGL
 platform is queried to determine extensions, capabilities and so
 forth. After rmPipeMakeCurrent(), your application can call 
 rmPipeGetNumMultitextureUnits() to determine the number of OpenGL
 texture units available. 

 Note that this routine does not perform any error checking with regard
 to ensuring that the value you provide for textureUnit does not exceed
 the number of texture units. That sort of problem will result in an
 runtime error that may be difficult to track down. It is the
 application's responsiblity to ensure that the value for texturenit is
 valid for the particular implementation. 
 
 Valid values for the "textureUnit" parameter are non-negative integers in the
 range 0..RM_MAX_MULTITEXTURES. Note that RM_MAX_MULTITEXTURES is a
 compile-time constant, and does not necessarily reflect the actual
 number of texture units supported by your OpenGL implementation.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneMultiTexture (RMnode *n,
			    RMtexture *t,
			    int textureUnit)
{
    RMenum rstat;

    if (RM_ASSERT(n, "rmNodeSetSceneMultiTexture() error: the input RMnode pointer is NULL.") == RM_WHACKED)
	return RM_WHACKED;

    /* sanity check the input value. Want something between zero and
     RM_MAX_MULTITEXTURES */
    if ((textureUnit < 0) || (textureUnit > (RM_MAX_MULTITEXTURES-1)))
    {
	char buf[256];
	sprintf(buf, "rmNodeSetSceneMultitexture error - the input value for the textureUnit parameter needs to be between 0 and %d.", RM_MAX_MULTITEXTURES-1);
	rmError(buf);
	return RM_WHACKED;
    }
    
    rstat = private_rmNodeSetSceneTexture(n, t, textureUnit);

    return rstat;
}

/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneTexture
 @pstart
 RMenum rmNodeGetSceneTexture (const RMnode *toQuery,
		               RMtexture **returnTexture)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 RMtexture **returnTexture - a handle to an RMtexture handle (return).
 @aend

 @dstart

 Use this routine to obtain the handle of the RMtexture scene parameter
 at an RMnode. If such a scene parameter is present, a handle to the
 internal RMtexture object is copied into caller-supplied memory
 (returnTexture), and RM_CHILL is returned on the stack. Otherwise,
 RM_WHACKED is returned, and caller-supplied memory remains unmodified.

 This routine will be useful for applications that will use the
 routine rmNodeUpdateSceneTexture to replace the contents of a
 particular texture, perhaps on a time-varying basis.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneTexture (const RMnode *n,
		       RMtexture **t)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneTexture() error: input RMnode pointer is NULL.") == RM_WHACKED) ||
	(RM_ASSERT(t, "rmNodeGetSceneTexture() error: input pointer to RMtexture pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);

    if ((n->scene_parms == NULL) || (n->scene_parms->textures[RM_MAX_MULTITEXTURES] == NULL))
	return(RM_WHACKED);

    *t = n->scene_parms->textures[RM_MAX_MULTITEXTURES];

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneMultiTexture
 @pstart
 RMenum rmNodeGetSceneMultiTexture (const RMnode *toQuery,
                                    int textureIndx,
		                    RMtexture **returnTexture)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 int textureIndx - an integer value (input).

 RMtexture **returnTexture - a handle to an RMtexture handle (return).
 @aend

 @dstart

 Similar in function and return values to rmNodeGetSceneTexture. This
 routine is used to obtain a handle to the RMtexture that is assigned to
 a specific multitexturing texture unit.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneMultiTexture (const RMnode *n,
			    int textureIndx,
			    RMtexture **t)
{
    if ((RM_ASSERT(n, "rmNodeGetSceneMultiTexture() error: input RMnode pointer is NULL.") == RM_WHACKED) ||
	(RM_ASSERT(t, "rmNodeGetSceneMultiTexture() error: input pointer to RMtexture pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);

    if ((textureIndx < 0) || (textureIndx > (RM_MAX_MULTITEXTURES-1)))
    {
	char buf[256];
	sprintf(buf,"rmNodeGetSceneMultiTexture error - the input textureIndx has a value of %d, but it should have a value in the range zero..%d \n", textureIndx, RM_MAX_MULTITEXTURES-1);
	rmWarning(buf);
	return RM_WHACKED;
    }

    if ((n->scene_parms == NULL) || (n->scene_parms->textures[textureIndx] == NULL))
	return(RM_WHACKED);

    *t = n->scene_parms->textures[textureIndx];

    return RM_CHILL;
}

/*
 * ----------------------------------------------------
 * @Name rmNodeSetSceneViewport
 @pstart
 RMenum rmNodeSetSceneViewport (RMnode *toModify,
			        const float *newViewport)
 @pend

 @astart
 RMnode *toModify - a handle to an RMnode (modified).

 const float *newViewport - a handle to an array of four floats, defining a
    viewport (input) [x, y, w, h].
 @aend

 @dstart

 The viewport scene parameter defines a pixel rectangle in the window
 into which the final image is mapped. The four viewport parameters,
 (x, y, width, height), are passed in as a single array of floats.
 The (x,y) coordinate specifies the lower left corner of the viewport
 rectangle.  Width and height are the size of the viewport. The input
 values are specified in normalized device coordinates:  Use input values
 of {0.0, 0.0, 1.0, 1.0} to consume the entire window; an NDC coordinate
 of (0.5, 0.5) refers to a point in the middle of the display window,
 regardless of its pixel dimensions.

 Use this routine to specify a viewport as a scene parameter at an
 RMnode. Upon success, any existing viewport at the RMnode is removed,
 then a copy of the input viewport parameters are made, then assigned
 to the RMnode toModify. Passing in a value of NULL for the viewport
 parameter will result in the removal of any existing viewport
 scene parameter from the RMnode.

 Upon success, RM_CHILL is returned; otherwise, RM_WHACKED is
 returned.

 Because this routine makes a copy of the input scene parameter (an
 viewport specification), callers do not need to manage the input object
 after a successful return from this routine. This has important
 ramificiations:

 1. Since a copy is made, any changes made to the caller's object will have
 no effect upon the scene graph, unless this routine is called again to
 update the scene parameter inside the scene graph;

 2. Callers may safely delete their copy of the input object after a 
 successful return from this routine.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeSetSceneViewport (RMnode *n,
			const float *vp) /* [x, y, w, h], ndc coords */
{
    if (RM_ASSERT(n, "rmNodeSetSceneViewport() error: the input RMnode pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    if (n->scene_parms->viewport != NULL)
    {
	rmFloatDelete(n->scene_parms->viewport);
	n->scene_parms->viewport = NULL;
    }

    if (vp != NULL)
    {
	n->scene_parms->viewport = rmFloatNew(4);
	memcpy((void *)(n->scene_parms->viewport), (void *)vp, (sizeof(float) * 4));
    }
    
    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmNodeGetSceneViewport
 @pstart
 RMenum rmNodeGetSceneViewport (const RMnode *toQuery,
			        float **returnViewport)
 @pend

 @astart
 const RMnode *toQuery - a handle to an RMnode (input).

 float **returnViewport - a handle to an array of float (returned).
 @aend

 @dstart

 This routine is used to query the viewport scene parameters at an
 RMnode. If the input RMnode toQuery has a viewport scene parameter,
 a copy of the viewport is created, then the handle to the copy returned
 to the caller through the parameter returnViewport, and RM_CHILL is
 returned on the stack.

 Otherwise, RM_WHACKED is returned and the caller supplied return
 parameter returnViewport remains unmodified.
 
 Because this routine returns a COPY of the RMnode's scene parameter (an
 array of floats), two important ramifications should be noted:

 1. Since a copy is returned, any changes made to the caller's object will have
 no effect upon the scene graph, unless the appropriate routine is called 
 again to update the scene parameter inside the scene graph;

 2. Callers are responsible for managing the returned object 
 (rmFloatDelete the object when it is no longer needed).

 @dend
 * ----------------------------------------------------
 */
RMenum
rmNodeGetSceneViewport (const RMnode *n,
			float **vp) /* [x, y, w, h] */
{
    if ((RM_ASSERT(n, "rmNodeGetSceneViewport() error: the input RMnode pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(vp, "rmNodeGetSceneViewport() error: the input pointer to a float pointer is NULL.") == RM_WHACKED))
	return(RM_WHACKED);
    
    if ((n->scene_parms == NULL) || (n->scene_parms->viewport == NULL))
	return(RM_WHACKED);

    *vp = rmFloatNew(4);
    memcpy((void *)(*vp), (void *)(n->scene_parms->viewport), sizeof(float)*4);
	
    return(RM_CHILL);
}


/* PRIVATE */
RMtextProps *
private_rmDefaultTextProps (RMpipe *p)
{
    /*
     * create a known and "default" font that will be used when the
     * app wants to draw text, but fails to provide an RMtextProps
     * object anywhere in the scene graph.
     */
    RMtextProps *tp;

    tp = rmTextPropsNew();
    rmTextPropsSetAttribs(tp, RM_FONT_SANS, RM_FONT_M, RM_FALSE, RM_FALSE, RM_LEFT, RM_BOTTOM);
    
    if (p != NULL)
	private_rmPrepareBitmapFont(tp,p);

    return(tp);
}


/* PRIVATE */
internals_RMsceneParms *
private_rmNodeSceneParmsNew (void)
{
    internals_RMsceneParms *t;
    
    t = (internals_RMsceneParms *)malloc(sizeof(internals_RMsceneParms));
    memset(t, 0, sizeof(internals_RMsceneParms));

    t->haveAnyTextures = RM_FALSE;

    return(t);
}

/* PRIVATE */
internals_RMfbClear *
private_rmFBClearNew (void)
{
    internals_RMfbClear *t;
    
    t = (internals_RMfbClear *)malloc(sizeof(internals_RMfbClear));
    memset(t, 0, sizeof(internals_RMfbClear));

    return(t);
}

/* PRIVATE */
RMenum
private_rmNodeSetSceneTexture(RMnode *n,
			      RMtexture *t,
			      int textureUnit)
{
    if (n->scene_parms == NULL)
	n->scene_parms = private_rmNodeSceneParmsNew();

    /* remove the old texture, if any */
    if (n->scene_parms->textures[textureUnit] != NULL)
    {
	int refCount = private_rmTextureGetRefCount(n->scene_parms->textures[textureUnit]);

	/*
	 * 11/21/03
	 * if the refCount is -1, then the texture isn't a shared texture
	 * and we should delete it. 
	 */
	if (refCount == -1)
	    rmTextureDelete(n->scene_parms->textures[textureUnit], RM_TRUE);
	else
	{
	    /*
	     * otherwise, we want to decrement the reference count, but
	     * clamp the new value to be >= 0. We won't automatically delete
	     * a shared texture - that is the application's responsibility.
	     */
	    refCount -= 1;
	    if (refCount < 0)
		refCount = 0;
	    private_rmTextureSetRefCount(n->scene_parms->textures[textureUnit], refCount);
	}
    }

    if (t != NULL)
    {
	/* update the RMtexture's context cache ID and data keys if they
	 are not uninitialized. */
	if (t->cacheKeyID == RM_CACHEKEY_UNINITIALIZED_VALUE)
	    private_rmTextureSetIDCacheKey(t);
	
	if (t->cacheKeyData == RM_CACHEKEY_UNINITIALIZED_VALUE)
	    private_rmTextureSetDataCacheKey(t);

	/* increment the texture's refcount */
	private_rmTextureSetRefCount(t, private_rmTextureGetRefCount(t) + 1);
	n->scene_parms->textures[textureUnit] = t;
    }
    else
	n->scene_parms->textures[textureUnit] = NULL;

    {
	int i;
	n->scene_parms->haveAnyTextures = RM_FALSE;
	for (i=0; i <= RM_MAX_MULTITEXTURES; i++)
	{
	    if (n->scene_parms->textures[i] != NULL)
	    {
		n->scene_parms->haveAnyTextures = RM_TRUE;
		break;
	    }
	}
    }

    private_rmNodeAttribMask(n,private_rmNodeComputeAttribMask(n),RM_SET);
    return RM_CHILL;
}

/* EOF */
