/*
 * 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: rmfog.c,v 1.5 2005/02/19 16:22:50 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.5 $
 * $Log: rmfog.c,v $
 * Revision 1.5  2005/02/19 16:22:50  wes
 * Distro sync and consolidation.
 *
 * Revision 1.4  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.3  2004/01/16 16:44:05  wes
 * Updated copyright line for 2004.
 *
 * 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.5  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.4  2002/04/30 19:31:39  wes
 * Updated copyright dates.
 *
 * Revision 1.3  2001/03/31 17:12:38  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.2  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * 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.
 *
 */

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

/*
 * ----------------------------------------------------
 * @Name rmFogNew
 @pstart
 RMfog * rmFogNew (void)
 @pend

 @astart
 No arguments.
 @aend

 @dstart

 Creates a new RMfog object, returning a handle to the caller upon
 success, or NULL upon failure.

 The RMfog object provides access to a visual simulation technique
 that approximates the atmospheric effect of light absorption and
 scattering. To enable fogging, applications must first create an
 RMfog object, set parameters to desired values, then assign the RMfog
 object as a scene parameter to an RMnode. All objects that are
 descendents in the scene graph of the RMnode containing the RMfog
 scene parameter will be rendered with fogging activated.

 rmFogNew assigns OpenGL defaults to the RMfog object. Specifically:

 Fogging mode is set to GL_EXP, fog density is set to 1.0, fog start
 and end are set to 0.0 and 1.0, and the fog color is set to
 (0,0,0,0).

 See the rmFogSet* family of routines for detailed descriptions of fog
 parameters. In addition, the OpenGL red book is a good reference.

 Use rmFogDelete to remove the RMfog object when no longer needed.

 @dend
 * ----------------------------------------------------
 */
RMfog *
rmFogNew (void)
{
    RMfog *f;
    RMcolor4D defColor={0.0F, 0.0F, 0.0F, 0.0F};
    
    f = (RMfog *)malloc(sizeof(RMfog));

    if (f == NULL)
    {
	rmWarning("rmFogNew() malloc failure.");
	return(NULL);
    }

    /* set defaults to be consistent with OpenGL defaults */
    rmFogSetMode(f, GL_EXP);
    rmFogSetDensity(f, (GLfloat)1.0);
    rmFogSetStartEnd(f, (GLfloat)0.0, (GLfloat)1.0);
    rmFogSetColor(f, &defColor);

    return(f);
}


/*
 * ----------------------------------------------------
 * @Name rmFogDup
 @pstart
 RMfog * rmFogDup (const RMfog *toDuplicate)
 @pend

 @astart
 const RMfog *toDuplicate - a handle to an RMfog object to duplicated
    (input).
 @aend

 @dstart

 Creates a new RMfog object, copying the parameters from an existing
 RMfog object into the new RMfog object, and returns a handle to the
 new object to the caller upon success.  Otherwise, NULL is returned
 upon failure.

 Use rmFogDelete to free the RMfog object when no longer needed.

 @dend
 * ----------------------------------------------------
 */
RMfog *
rmFogDup (const RMfog *toDuplicate)
{
    RMfog *f;

    if (RM_ASSERT(toDuplicate, "rmFogDup() error: the input RMfog pointer is NULL.") == RM_WHACKED)
	return(NULL);
    
    f = rmFogNew();

    if (f != NULL)
	*f = *toDuplicate;
    return(f);
}


/*
 * ----------------------------------------------------
 * @Name rmFogDelete
 @pstart
 RMenum rmFogDelete (RMfog *toDelete)
 @pend

 @astart
 RMfog *toDelete - a handle to an RMfog object to be deleted
    (modified).
 @aend

 @dstart

 Use this routine to release resources associated with an RMfog
 object.  Returns RM_CHILL upon success, or RM_WHACKED upon failure.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogDelete (RMfog *f)
{
    if (RM_ASSERT(f, "rmFogDelete() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    free((void *)f);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmFogSetMode
 @pstart
 RMenum rmFogSetMode (RMfog *toModify,
	              GLenum newMode)
 @pend

 @astart
 RMfog *toModify - a handle to an RMfog object to modify (modified).

 GLenum newMode - an OpenGL fog mode enumerator. Must be one of
    GL_LINEAR, GL_EXP or GL_EXP2.
 @aend

 @dstart

 Use this routine to set the fogging mode of an RMfog object. Returns
 RM_CHILL upon success, or RM_WHACKED upon failure.

 Objects that are rendered using fogging appear to fade into the fog
 color (rmFogSetColor) as a function of distance from the
 viewpoint. Objects closer to the viewer are rendered with less fog
 color, objects further away appear to be rendered with more fog
 color.

 The three fogging parameters that may be controlled by applications
 are the fog color (rmFogSetColor), the front and back boundaries of
 the fogging itself (rmFogSetStartEnd) and the weighting function used
 for computing fog density (rmFogSetMode).

 The fogging functions are specified as one of GL_LINEAR, GL_EXP and
 GL_EXP2. In all methods, fog density is computed using the fog start
 and fog end, along with the Z coordinate of the object (pixel
 fragment) in eye-coordinates (after the model and view
 transformations have been applied, but before the projection matrix).

 In GL_LINEAR fogging, the fog density is computed as fogDensity =
 (fogEnd - Z)/(fogEnd-fogStart), then clamped to the range [0..1]. A
 value of Z that is less than fogStart will result in no fogging,
 while a value of Z that is greater than fogEnd will be fully
 fogged. Z values between fogEnd and fogStart are scaled linearly
 between 0.0 and 1.0.

 In GL_EXP fogging, the fog density is computed as fogDensity = e ^
 (-density * z), where density is one of the fogging parameters that
 may be controlled by applications.

 GL_EXP2 fogging is computed as fogDensity = e ^ ((-density * z)^2).

 The OpenGL red book has a nice picture of a graph showing the
 response curves of these functions.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogSetMode (RMfog *f,
	      GLenum newMode)
{
    if (RM_ASSERT(f, "rmFogSetMode() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    /* enum check on newMode? */
    f->fogMode = newMode;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmFogGetMode
 @pstart
 GLenum rmFogGetMode (const RMfog *toQuery)
 @pend

 @astart
 const RMfog *toQuery - a handle to an RMfog object to query (input).
 @aend

 @dstart

 Use this routine to obtain the current fogging mode of an RMfog
 object.  Returns one of GL_LINEAR, GL_EXP or GL_EXP2 upon success, or
 GL_INVALID_VALUE upon failure.
 
 @dend
 * ----------------------------------------------------
 */
GLenum
rmFogGetMode (const RMfog *f)
{
    if (RM_ASSERT(f, "rmFogGetMode() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(GL_INVALID_VALUE);

    return(f->fogMode);
}


/*
 * ----------------------------------------------------
 * @Name rmFogSetColor
 @pstart
 RMenum rmFogSetColor (RMfog *toModify,
	               const RMcolor4D *newColor)
 @pend

 @astart
 RMfog *toModify - a handle to an RMfog object to modify (modified).

 RMcolor4D *newColor - a handle to an RMcolor4D object (input).
 @aend

 @dstart

 This routine copies a color from the caller-supplied RMcolor4D object
 into the RMfog object, returning RM_CHILL upon success. RM_WHACKED
 indicates failure.

 The fog color is a 4-component RGBA tuple. When fogging is enabled,
 objects that are further away from the viewer will appear to be more
 consumed by the fog, or appear in more of the fog color and in less
 of their own color. Objects closer to the viewer will appear to be
 more visible, and be shaded with less fog color.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogSetColor (RMfog *f,
	       const RMcolor4D *newColor)
{
    if ((RM_ASSERT(f, "rmFogSetColor() error: the input RMfog pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(newColor, "rmFogSetColor() error: the input RMcolor4D pointer is NULL") == RM_WHACKED))
	return(RM_WHACKED);

    f->fogColor = *newColor;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmFogGetColor
 @pstart
 RMenum rmFogGetColor (const RMfog *toQuery,
	               RMcolor4D *returnColor)
 @pend

 @astart
 const RMfog *toQuery - a handle to an RMfog object to query (input).

 RMcolor4D *returnColor - a handle to an RMcolor4D object (modified).
 @aend

 @dstart

 Copies the fog color from an RMfog object into caller-supplied
 memory, returning RM_CHILL upon success. Otherwise, RM_WHACKED is
 returned.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogGetColor (const RMfog *f,
	       RMcolor4D *returnColor)
{
    if ((RM_ASSERT(f, "rmFogGetColor() error: the input RMfog pointer is NULL") == RM_WHACKED) ||
	(RM_ASSERT(returnColor, "rmFogGetColor() error: the input RMcolor4D pointer is NULL") == RM_WHACKED))
	return(RM_WHACKED);

    *returnColor = f->fogColor;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmFogSetDensity
 @pstart
 RMenum rmFogSetDensity (RMfog *toModify,
		         GLfloat newDensity)
 @pend

 @astart
 RMfog *toModify - a handle to an RMfog object to modify (modified).

 GLfloat newDensity - a floating point value in (input)
 @aend

 @dstart

 Assigns a new value to the fog density attribute of an RMfog object,
 returning RM_CHILL upon success, or RM_WHACKED upon failure.

 Fog density controls the relative thickness of the fog. Thicker, or
 more dense fog, will tend to obscure objects more quickly than
 lighter, or less dense fog. Density values should probably be in the
 range 0.0 to 1.0, although the specification is not clear on this
 matter.

 Fog density has no effect when the fog mode is set to GL_LINEAR.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogSetDensity (RMfog *f,
		 GLfloat newDensity)
{
    if (RM_ASSERT(f, "rmFogSetDensity() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    f->fogDensity = newDensity;

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmFogGetDensity
 @pstart
 GLfloat rmFogGetDensity (const RMfog *toQuery)
 @pend

 @astart
 const RMfog *toQuery - a handle to an RMfog object to query (input).
 @aend

 @dstart

 Returns a floating point value representing the fog density parameter
 of an RMfog object. Upon failure, an error message is issued and 1.0
 is returned.

 See rmFogSetDensity for an explanation of this parameter.

 @dend
 * ----------------------------------------------------
 */
GLfloat
rmFogGetDensity (const RMfog *f)
{
    if (RM_ASSERT(f, "rmFogGetDensity() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    return(f->fogDensity);
}


/*
 * ----------------------------------------------------
 * @Name rmFogSetStartEnd
 @pstart
 RMenum rmFogSetStartEnd (RMfog *toModify,
		          GLfloat newStart,
			  GLfloat newEnd)
 @pend

 @astart
 RMfog *toModify - a handle to an RMfog object to modify (modified).

 GLfloat newStart, newEnd - floating point values that specify the
    z-value of starting and ending point of the fog, respectively, in
    eye-coordinates (input). 
 @aend

 @dstart

 Assigns new values to the fog start and end attributes of an RMfog
 object, returning RM_CHILL upon success. Upon failure, RM_WHACKED is
 returned and the start/end parameters are not modified.

 The fog start and end parameters define a region of space in which
 fogging occurs. The most confusing thing about fogging (to me,
 anyway) is that the values for fog start and end are z-coordinate
 values in eye coordinates. They are not distances from the
 viewer. Fogging is applied at the pixel fragment level, not when
 vertices are shaded.  A pixel fragment is generated with an
 eye-coordinate z-value that is then later projected using the
 projection matrix. The z-coordinate of the pixel fragment in eye
 coordinates is used as input to the fogging equations (described in
 rmFogSetMode).

 Pixel fragments with a eye-space z-coordinate that are less than the
 "fog start" value will not be fogged. Those with z-coordinates
 greater than the "fog end" value will be fully fogged (subject to the
 limits of the fog function and fog density parameters; it is possible
 for a pixel with a z-coordinate greater than "fog end" to not be
 fully fogged due to the asymptotic behavior of the inverse
 exponential function).

 Intuitively, these values can be thought of as distances. Set the fog
 start value to be the approximate distance, along the line of sight,
 where you want fogging to begin. Likewise, the the fog end value to
 some point further away where you want full fogging saturation to
 occur.

 (Need to double-check this discussion, Jan 2000)

 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogSetStartEnd (RMfog *f,
		  GLfloat newStart,
		  GLfloat newEnd)
{
    if (RM_ASSERT(f, "rmFogSetStartEnd() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    f->fogStart = newStart;
    f->fogEnd = newEnd;
    
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmFogGetStartEnd
 @pstart
 RMenum rmFogGetStartEnd (const RMfog *toModify,
		          GLfloat *startReturn,
			  GLfloat *endReturn)
 @pend

 @astart
 const RMfog *toModify - a handle to an RMfog object to query (input).

 GLfloat *startReturn, *endReturn - handles to caller-supplied
    GLfloat's (modified). Use of NULL is acceptable. 
 @aend

 @dstart

 Use this routine to obtain the "fog start" and "fog end" attributes
 from an RMfog object. Upon success, the fog start and end attributes
 are copied into caller supplied memory, and RM_CHILL is
 returned. Otherwise, RM_WHACKED is returned.

 Callers may specify NULL for either return parameter, in which case
 that attribute will not be returned to the caller.

 See rmFogSetStartEnd for more information about these RMfog
 attributes.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmFogGetStartEnd (const RMfog *f,
		  GLfloat *startReturn,
		  GLfloat *endReturn)
{
    if (RM_ASSERT(f, "rmFogGetStartEnd() error: the input RMfog pointer is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (startReturn != NULL)
	*startReturn = f->fogStart;
    
    if (endReturn != NULL)
	*endReturn = f->fogEnd;
    
    return(RM_CHILL);
}
/* EOF */
