/* $Id: fog_tmp.h,v 1.4.4.2 2000/11/01 23:59:20 brianp Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  3.4
 * 
 * Copyright (C) 1999-2000  Brian Paul   All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/* For 3.2: Add a helper function for drivers to do fog coordinate
 * calculation.  Not called from standard pipelines.  
 */
static void TAG(make_fog_coord)( struct vertex_buffer *VB,
				 const GLvector4f *eye,
				 GLubyte flag)
{
   const GLcontext *ctx = VB->ctx;
   GLfloat end  = ctx->Fog.End;
   GLubyte *cullmask = VB->CullMask + VB->Start;
   GLfloat *v = eye->start;
   GLuint stride = eye->stride;
   GLuint n = VB->Count - VB->Start;
   GLubyte (*out)[4];		
   GLfloat d;
   GLuint i;

   (void) cullmask;
   (void) flag;

   /* Use specular alpha (front side) as fog coordinate.
    */
   out = VB->Spec[0] + VB->Start;

   if (VB->EyePtr->size > 2) {
      switch (ctx->Fog.Mode) {
      case GL_LINEAR:
	 d = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride)) {
	    CULLCHECK {
	       GLfloat f = (end - ABSF(v[2])) * d;
	       FLOAT_COLOR_TO_UBYTE_COLOR(out[i][3], f);
	    }
	 }
	 break;
      case GL_EXP:
	 d = -ctx->Fog.Density;
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v,stride)) {
	    CULLCHECK {
	       GLfloat f = exp( d*ABSF(v[2]) ); /* already clamped */ 
	       FLOAT_COLOR_TO_UBYTE_COLOR(out[i][3], f);
	    }
	 }
	 break;
      case GL_EXP2:
	 d = -(ctx->Fog.Density*ctx->Fog.Density);
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride)) {
	    CULLCHECK {
	       GLfloat z = v[2];
	       GLfloat f = exp( d*z*z ); /* already clamped */
	       FLOAT_COLOR_TO_UBYTE_COLOR(out[i][3], f);
	    }
	 }
	 break;
      default:
	 gl_problem(ctx, "Bad fog mode in make_fog_coord");
	 return;
      }
   }
   else 
   {
      GLubyte r = 255;

      if (ctx->Fog.Mode == GL_LINEAR) {
	 GLfloat f = 1.0 - ctx->Fog.End / (ctx->Fog.End - ctx->Fog.Start);
	 CLAMP_FLOAT_COLOR( f );
	 FLOAT_COLOR_TO_UBYTE_COLOR(r, f);
      } 

      for (i = 0 ; i < n ; i++) 
	 out[i][3] = r;
   }
}





#if 0
/* TODO : use fog coordinates as intermediate step in all fog
 * calculations.
 */

static void TAG(fog_rgba_vertices)( struct vertex_buffer *VB,
				    GLuint side,
				    GLubyte flag)
{
   const GLcontext *ctx = VB->ctx;
   const GLubyte rFog = ctx->Fog.ByteColor[0];
   const GLubyte gFog = ctx->Fog.ByteColor[1];
   const GLubyte bFog = ctx->Fog.ByteColor[2];
   GLfloat end  = ctx->Fog.End;
   GLubyte *cullmask = VB->CullMask + VB->Start;
   GLubyte (*fcoord)[4] = VB->SpecPtr[0]->start;
   GLuint stride = VB->SpecPtr[0]->stride;
   GLuint n = VB->Count - VB->Start;
   GLubyte *in;
   GLuint in_stride;
   GLubyte (*out)[4];
   GLfloat d,t;
   GLuint i;

   (void) cullmask;
   (void) flag;

   /* Get correct source and destination for fogged colors.
    */
   in_stride = VB->Color[side]->stride;
   in = VB->Color[side]->start;
   VB->Color[side] = VB->FoggedColor[side];
   VB->ColorPtr = VB->Color[0];
   out = (GLubyte (*)[4])VB->Color[side]->start;

   FLOAT_COLOR_TO_UBYTE_COLOR( rFog, ctx->Fog.Color[0] );

   for ( i = 0 ; i < n ; i++, STRIDE_F(spec, sp_stride), in += in_stride) {
      CULLCHECK {
	 GLint fc = (GLint) spec[3], ifc = 255 - fc;
	 
	 out[i][0] = (fc * in[0] + ifc * rFog) >> 8;
	 out[i][1] = (fc * in[1] + ifc * gFog) >> 8;
	 out[i][2] = (fc * in[2] + ifc * bFog) >> 8;
      }
   }
}



static void TAG(fog_ci_vertices)( struct vertex_buffer *VB,
				  GLuint side,
				  GLubyte flag )
{
   GLcontext *ctx = VB->ctx;

   GLubyte *cullmask = VB->CullMask + VB->Start;
  
   GLfloat *v = VB->EyePtr->start;
   GLuint stride = VB->EyePtr->stride;
   GLuint vertex_size = VB->EyePtr->size;
   GLuint n = VB->EyePtr->count;

   GLuint *in;
   GLuint in_stride;
   GLuint *out;
   GLuint i;

   (void) flag;
   (void) cullmask;


   in = VB->Index[side]->start;
   in_stride = VB->Index[side]->stride;
   VB->IndexPtr = VB->FoggedIndex[side];
   out = VB->IndexPtr->start;


   /* NOTE: the use of casts generates better/faster code for MIPS */
   for ( i = 0; i < n ; i++, STRIDE_F(v,stride), STRIDE_UI(in,in_stride))
      CULLCHECK {
      GLfloat f = (fogend - ABSF(v[2])) * d;
      f = CLAMP( f, 0.0, 1.0 );
      *out = (GLint) ((GLfloat)(GLint) *in + (1.0F-f) * fogindex);
   }
}

#endif




static void TAG(fog_rgba_vertices)( struct vertex_buffer *VB,
				    GLuint side,
				    GLubyte flag)
{
   const GLcontext *ctx = VB->ctx;
   GLfloat rFog = ctx->Fog.Color[0];
   GLfloat gFog = ctx->Fog.Color[1];
   GLfloat bFog = ctx->Fog.Color[2];
   GLfloat end  = ctx->Fog.End;
   GLubyte *cullmask = VB->CullMask + VB->Start;
   GLfloat *v = VB->EyePtr->start;
   GLuint stride = VB->EyePtr->stride;
   GLuint n = VB->EyePtr->count;
   GLubyte *in;
   GLuint in_stride;
   GLubyte (*out)[4];
   GLfloat d,t;
   GLuint i;

   (void) cullmask;
   (void) flag;

   /* Get correct source and destination for fogged colors.
    */
   in_stride = VB->Color[side]->stride;
   in = VB->Color[side]->start;
   VB->Color[side] = VB->FoggedColor[side];
   VB->ColorPtr = VB->Color[0];
   out = (GLubyte (*)[4])VB->Color[side]->start;

   if (VB->EyePtr->size > 2) {
      switch (ctx->Fog.Mode) {
      case GL_LINEAR:
	 d = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride), in += in_stride) {
	    CULLCHECK {
	       GLfloat f = (end - ABSF(v[2])) * d;
               if (f >= 1.0) {
                  out[i][0] = in[0];
                  out[i][1] = in[1];
                  out[i][2] = in[2];
               }
	       else if (f <= 0.0) {
		  CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][0], rFog);
		  CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][1], gFog);
		  CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][2], bFog);
	       }
               else {
		  t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[0]) + (1.0F-f)*rFog;
		  CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][0], t);
	       
		  t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[1]) + (1.0F-f)*gFog;
		  CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][1], t);
	       
		  t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[2]) + (1.0F-f)*bFog;
		  CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][2], t);

	       }
	    }
	 }
	 break;
      case GL_EXP:
	 d = -ctx->Fog.Density;
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v,stride), in += in_stride) {
	    CULLCHECK {
	       GLfloat f = exp( d*ABSF(v[2]) ); /* already clamped */ 

	       t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[0]) + (1.0F-f)*rFog;
	       CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][0], t);
	       
	       t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[1]) + (1.0F-f)*gFog;
	       CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][1], t);
	       
	       t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[2]) + (1.0F-f)*bFog;
	       CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][2], t);
	    }
	 }
	 break;
      case GL_EXP2:
	 d = -(ctx->Fog.Density*ctx->Fog.Density);
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride), in += in_stride) {
	    CULLCHECK {
	       GLfloat z = v[2];
	       GLfloat f = exp( d*z*z ); /* already clamped */

	       t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[0]) + (1.0F-f)*rFog;
	       CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][0], t);
	       
	       t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[1]) + (1.0F-f)*gFog;
	       CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][1], t);
	       
	       t = f * UBYTE_COLOR_TO_FLOAT_COLOR(in[2]) + (1.0F-f)*bFog;
	       CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(out[i][2], t);
	    }
	 }
	 break;
	 default:
	    gl_problem(ctx, "Bad fog mode in gl_fog_rgba_vertices");
	 return;
      }
   }
   else {
      /* All vertex Z coordinates are zero */
      if (ctx->Fog.Mode == GL_LINEAR) {
         /* 2-vector vertices */
         GLubyte r,g,b;
         GLfloat f = ctx->Fog.End * (ctx->Fog.End - ctx->Fog.Start);
         CLAMP_FLOAT_COLOR( f );
         f = 1.0 - f;
         rFog *= f;
         bFog *= f;
         gFog *= f;

         CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(r, rFog);
         CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(g, gFog);
         CLAMPED_FLOAT_COLOR_TO_UBYTE_COLOR(b, bFog);

         for (i = 0 ; i < n ; i++) {
            /* CULLCHECK */ {
               out[i][0] = r;
               out[i][1] = g;
               out[i][2] = b;
            }
         }
      }
      else {
         /* EXP or EXP2 mode */
         /* f = exp(d*z*z) or f = exp(d*|z|) is one.  Copy in color to out */
	 for ( i = 0 ; i < n ; i++, STRIDE_F(v,stride), in += in_stride) {
            /* CULLCHECK */ {
               out[i][0] = in[0];
               out[i][1] = in[1];
               out[i][2] = in[2];
            }
         }
      }
   }
}



/*
 * Compute the fogged color indexes for an array of vertices.
 * Input:  n - number of vertices
 *         v - array of vertices
 * In/Out: indx - array of vertex color indexes
 */
static void TAG(fog_ci_vertices)( struct vertex_buffer *VB,
				  GLuint side,
				  GLubyte flag )
{
   GLcontext *ctx = VB->ctx;

   GLubyte *cullmask = VB->CullMask + VB->Start;
  
   GLfloat *v = VB->EyePtr->start;
   GLuint stride = VB->EyePtr->stride;
   GLuint vertex_size = VB->EyePtr->size;
   GLuint n = VB->EyePtr->count;

   GLuint *in;
   GLuint in_stride;
   GLuint *out;
   GLuint i;

   (void) flag;
   (void) cullmask;


   in = VB->Index[side]->start;
   in_stride = VB->Index[side]->stride;
   VB->IndexPtr = VB->FoggedIndex[side];
   out = VB->IndexPtr->start;


   /* NOTE: the extensive use of casts generates better/faster code for MIPS */
   if (vertex_size > 2) {
      switch (ctx->Fog.Mode) {
      case GL_LINEAR:
      {
	 GLfloat d = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
	 GLfloat fogindex = ctx->Fog.Index;
	 GLfloat fogend = ctx->Fog.End;

 	 for ( i = 0; i < n ; i++, STRIDE_F(v,stride), STRIDE_UI(in,in_stride))
	 {
	    CULLCHECK {
	       GLfloat f = (fogend - ABSF(v[2])) * d;
	       f = CLAMP( f, 0.0, 1.0 );
	       *out = (GLint) ((GLfloat)(GLint) *in + (1.0F-f) * fogindex);
	    }
	 }
	 break;
      }
      case GL_EXP:
      {
	 GLfloat d = -ctx->Fog.Density;
	 GLfloat fogindex = ctx->Fog.Index;
 	 for ( i = 0; i < n ; i++, STRIDE_F(v,stride), STRIDE_UI(in,in_stride))
	 {
	    CULLCHECK {
	       GLfloat f = exp( d * ABSF(v[2]) );
	       *out = (GLint) ((GLfloat)(GLint) *in + (1.0F-f) * fogindex);
	    }
	 }
	 break;
      }
      case GL_EXP2:
      {
	 GLfloat d = -(ctx->Fog.Density*ctx->Fog.Density);
	 GLfloat fogindex = ctx->Fog.Index;
 	 for ( i = 0; i < n ; i++, STRIDE_F(v,stride),STRIDE_UI(in,in_stride))
	 {
	    CULLCHECK {
	       GLfloat z = v[2]; /*ABSF(v[i][2]);*/
	       GLfloat f = exp( d * z*z ); /* was -d ??? */
	       out[i] = (GLint) ((GLfloat)(GLint) *in + (1.0F-f) * fogindex);
	    }
	 }
	 break;
      }
      default:
	 gl_problem(ctx, "Bad fog mode in gl_fog_ci_vertices");
	 return;
      }
   } else if (ctx->Fog.Mode == GL_LINEAR) {
      GLuint fogindex;
      GLfloat f = ctx->Fog.End / (ctx->Fog.End - ctx->Fog.Start);

      f = 1.0 - CLAMP( f, 0.0F, 1.0F );
      fogindex = (GLuint)(f * ctx->Fog.Index);
      if (fogindex) {
 	 for ( i = 0; i < n ; i++, STRIDE_F(v,stride), STRIDE_UI(in,in_stride))
	 {
	    CULLCHECK {
	       *out = *in + fogindex;
	    }
	 }
      }
   } 
}

static void TAG(init_fog_tab)(void)
{
   fog_ci_tab[IDX] = TAG(fog_ci_vertices);
   fog_rgba_tab[IDX] = TAG(fog_rgba_vertices);
   make_fog_coord_tab[IDX] = TAG(make_fog_coord);
}

#undef TAG
#undef CULLCHECK
#undef IDX
