/* fog.c */

/*
 * Mesa 3-D graphics library
 * Version:  1.2
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
$Id: fog.c,v 1.7 1995/06/12 15:41:04 brianp Exp $

$Log: fog.c,v $
 * Revision 1.7  1995/06/12  15:41:04  brianp
 * removed debugging printf's
 *
 * Revision 1.6  1995/06/12  15:33:54  brianp
 * changed color arrays to GLubyte
 * check for -eyez in gl_fog_index_pixels
 *
 * Revision 1.5  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.4  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.3  1995/02/27  22:48:50  brianp
 * modified for PB
 *
 * Revision 1.2  1995/02/26  22:58:24  brianp
 * fixed glFogi
 *
 * Revision 1.1  1995/02/24  14:23:03  brianp
 * Initial revision
 *
 */


#include <math.h>
#include "context.h"
#include "list.h"
#include "macros.h"



void gl_fog( GLenum pname, const GLfloat *params )
{
   GLenum m;

   switch (pname) {
      case GL_FOG_MODE:
         m = (GLenum) (GLint) *params;
	 if (m==GL_LINEAR || m==GL_EXP || m==GL_EXP2) {
	    CC.Fog.Mode = m;
	 }
	 else {
	    gl_error( GL_INVALID_ENUM, "glFog" );
	 }
	 break;
      case GL_FOG_DENSITY:
	 if (*params<0.0) {
	    gl_error( GL_INVALID_VALUE, "glFog" );
	 }
	 else {
	    CC.Fog.Density = *params;
	 }
	 break;
      case GL_FOG_START:
	 CC.Fog.Start = *params;
	 break;
      case GL_FOG_END:
	 CC.Fog.End = *params;
	 break;
      case GL_FOG_INDEX:
	 CC.Fog.Index = *params;
	 break;
      case GL_FOG_COLOR:
	 CC.Fog.Color[0] = params[0];
	 CC.Fog.Color[1] = params[1];
	 CC.Fog.Color[2] = params[2];
	 CC.Fog.Color[3] = params[3];
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glFog" );
   }
}



void glFogf( GLenum pname, GLfloat param )
{
   if (CC.ExecuteFlag) {
      gl_fog( pname, &param );
   }
   if (CC.CompileFlag) {
      gl_save_fog( pname, &param );
   }
}



void glFogi( GLenum pname, GLint param )
{
   GLfloat p;

   p = (GLfloat) param;

   if (CC.ExecuteFlag) {
      gl_fog( pname, &p );
   }
   if (CC.CompileFlag) {
      gl_save_fog( pname, &p );
   }
}



void glFogfv( GLenum pname, const GLfloat *params )
{
   if (CC.ExecuteFlag) {
      gl_fog( pname, params );
   }
   if (CC.CompileFlag) {
      gl_save_fog( pname, params );
   }
}



void glFogiv( GLenum pname, const GLint *params )
{
   GLfloat p[4];

   switch (pname) {
      case GL_FOG_MODE:
      case GL_FOG_DENSITY:
      case GL_FOG_START:
      case GL_FOG_END:
      case GL_FOG_INDEX:
         p[0] = (GLfloat) *params;
	 break;
      case GL_FOG_COLOR:
	 p[0] = INT_TO_FLOAT( params[0] );
	 p[1] = INT_TO_FLOAT( params[1] );
	 p[2] = INT_TO_FLOAT( params[2] );
	 p[3] = INT_TO_FLOAT( params[3] );
	 break;
      default:
	 gl_error( GL_INVALID_ENUM, "glFogi" );
	 return;
   }
   if (CC.ExecuteFlag) {
      gl_fog( pname, p );
   }
   if (CC.CompileFlag) {
      gl_save_fog( pname, p );
   }
}



/*
 * Compute the fogged color for a vertex.
 * Input:  z - distance from origin to eye coordinate being fogged.
 *         color - the original vertex color
 * Output:  color - the fogged color
 */
void gl_fog_color_vertex( GLfloat z, GLfloat color[4] )
{
   GLfloat f;

   switch (CC.Fog.Mode) {
      case GL_LINEAR:
         f = (CC.Fog.End - z) / (CC.Fog.End - CC.Fog.Start);
	 break;
      case GL_EXP:
	 f = exp( -CC.Fog.Density * z );
	 break;
      case GL_EXP2:
	 f = exp( -(CC.Fog.Density*CC.Fog.Density * z*z) );
	 break;
   }

   f = CLAMP( f, 0.0F, 1.0F );

   color[0] = f * color[0] + (1.0-f) * CC.Fog.Color[0];
   color[1] = f * color[1] + (1.0-f) * CC.Fog.Color[1];
   color[2] = f * color[2] + (1.0-f) * CC.Fog.Color[2];
   color[3] = f * color[3] + (1.0-f) * CC.Fog.Color[3];
}



/*
 * Compute the fogged color index for a vertex.
 * Input:  z - distance from origin to eye coordinate being fogged.
 *         index - the original vertex color index
 * Output:  index - the fogged color index
 */
void gl_fog_index_vertex( GLfloat z, GLfloat *indx )
{
   GLfloat f;

   switch (CC.Fog.Mode) {
      case GL_LINEAR:
         f = (CC.Fog.End - z) / (CC.Fog.End - CC.Fog.Start);
	 break;
      case GL_EXP:
	 f = exp( -CC.Fog.Density * z );
	 break;
      case GL_EXP2:
	 f = exp( -(CC.Fog.Density*CC.Fog.Density * z*z) );
	 break;
   }

   f = CLAMP( f, 0.0F, 1.0F );

   *indx = *indx + (1.0-f) * CC.Fog.Index;
}




/*
 * Apply fog to an array of RGBA pixels.
 * Input:  n - number of pixels
 *         z - array of integer depth values
 *         red, green, blue, alpha - pixel colors
 * Output:  red, green, blue, alpha - fogged pixel colors
 */
void gl_fog_color_pixels( GLuint n, GLint z[], GLubyte red[],
			  GLubyte green[], GLubyte blue[], GLubyte alpha[] )
{
   GLfloat d = CC.ProjectionMatrix[14];
   GLfloat c = CC.ProjectionMatrix[10];
   GLfloat winz, ndcz, eyez, f;
   GLuint i;

   GLint fog_red   = (GLint) (CC.Fog.Color[0] * CC.RedScale);
   GLint fog_green = (GLint) (CC.Fog.Color[1] * CC.GreenScale);
   GLint fog_blue  = (GLint) (CC.Fog.Color[2] * CC.BlueScale);
   GLint fog_alpha = (GLint) (CC.Fog.Color[3] * CC.AlphaScale);

   switch (CC.Fog.Mode) {
      case GL_LINEAR:
         for (i=0;i<n;i++) {
	    winz = (GLfloat) z[i] / (GLfloat) MAX_DEPTH;
	    ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;
	    eyez = -d / (c+ndcz);
	    if (eyez < 0.0)  eyez = -eyez;
	    f = (CC.Fog.End - eyez) / (CC.Fog.End - CC.Fog.Start);
	    f = CLAMP( f, 0.0F, 1.0F );
	    red[i]   = (GLint) (f * red[i]   + (1.0-f) * fog_red);
	    green[i] = (GLint) (f * green[i] + (1.0-f) * fog_green);
	    blue[i]  = (GLint) (f * blue[i]  + (1.0-f) * fog_blue);
	    alpha[i] = (GLint) (f * alpha[i] + (1.0-f) * fog_alpha);
	 }
	 break;
      case GL_EXP:
	 for (i=0;i<n;i++) {
	    winz = (GLfloat) z[i] / (GLfloat) MAX_DEPTH;
	    ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;
	    eyez = -d / (c+ndcz);
	    if (eyez < 0.0)  eyez = -eyez;
	    f = exp( -CC.Fog.Density * eyez );
	    f = CLAMP( f, 0.0F, 1.0F );
	    red[i]   = (GLint) (f * red[i]   + (1.0-f) * fog_red);
	    green[i] = (GLint) (f * green[i] + (1.0-f) * fog_green);
	    blue[i]  = (GLint) (f * blue[i]  + (1.0-f) * fog_blue);
	    alpha[i] = (GLint) (f * alpha[i] + (1.0-f) * fog_alpha);
	 }
	 break;
      case GL_EXP2:
	 for (i=0;i<n;i++) {
	    winz = (GLfloat) z[i] / (GLfloat) MAX_DEPTH;
	    ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;
	    eyez = -d / (c+ndcz);
	    if (eyez < 0.0)  eyez = -eyez;
	    f = exp( -(CC.Fog.Density*CC.Fog.Density * eyez*eyez) );
	    f = CLAMP( f, 0.0F, 1.0F );
	    red[i]   = (GLint) (f * red[i]   + (1.0-f) * fog_red);
	    green[i] = (GLint) (f * green[i] + (1.0-f) * fog_green);
	    blue[i]  = (GLint) (f * blue[i]  + (1.0-f) * fog_blue);
	    alpha[i] = (GLint) (f * alpha[i] + (1.0-f) * fog_alpha);
	 }
	 break;
   }
}




/*
 * Apply fog to an array of color index pixels.
 * Input:  n - number of pixels
 *         z - array of integer depth values
 *         index - pixel color indexes
 * Output:  index - fogged pixel color indexes
 */
void gl_fog_index_pixels( GLuint n, GLint z[], GLuint index[] )
{
   GLfloat d = CC.ProjectionMatrix[14];
   GLfloat c = CC.ProjectionMatrix[10];
   GLfloat winz, ndcz, eyez, f;
   GLuint i;

   switch (CC.Fog.Mode) {
      case GL_LINEAR:
         for (i=0;i<n;i++) {
	    winz = (GLfloat) z[i] / (GLfloat) MAX_DEPTH;
	    ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;
	    eyez = -d / (c+ndcz);
	    if (eyez < 0.0)  eyez = -eyez;
	    f = (CC.Fog.End - eyez) / (CC.Fog.End - CC.Fog.Start);
	    f = CLAMP( f, 0.0F, 1.0F );
	    index[i] = (GLuint) ((GLfloat) index[i] + (1.0-f) * CC.Fog.Index);
	 }
	 break;
      case GL_EXP:
         for (i=0;i<n;i++) {
	    winz = (GLfloat) z[i] / (GLfloat) MAX_DEPTH;
	    ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;
	    eyez = -d / (c+ndcz);
	    if (eyez < 0.0)  eyez = -eyez;
	    f = exp( -CC.Fog.Density * eyez );
	    f = CLAMP( f, 0.0F, 1.0F );
	    index[i] = (GLuint) ((GLfloat) index[i] + (1.0-f) * CC.Fog.Index);
	 }
	 break;
      case GL_EXP2:
         for (i=0;i<n;i++) {
	    winz = (GLfloat) z[i] / (GLfloat) MAX_DEPTH;
	    ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;
	    eyez = -d / (c+ndcz);
	    if (eyez < 0.0)  eyez = -eyez;
	    f = exp( -(CC.Fog.Density*CC.Fog.Density * eyez*eyez) );
	    f = CLAMP( f, 0.0F, 1.0F );
	    index[i] = (GLuint) ((GLfloat) index[i] + (1.0-f) * CC.Fog.Index);
	 }
	 break;
   }

}

