17117f1b4Smrg/*
27117f1b4Smrg * Mesa 3-D graphics library
37117f1b4Smrg *
47117f1b4Smrg * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
57117f1b4Smrg *
67117f1b4Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77117f1b4Smrg * copy of this software and associated documentation files (the "Software"),
87117f1b4Smrg * to deal in the Software without restriction, including without limitation
97117f1b4Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
107117f1b4Smrg * and/or sell copies of the Software, and to permit persons to whom the
117117f1b4Smrg * Software is furnished to do so, subject to the following conditions:
127117f1b4Smrg *
137117f1b4Smrg * The above copyright notice and this permission notice shall be included
147117f1b4Smrg * in all copies or substantial portions of the Software.
157117f1b4Smrg *
167117f1b4Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177117f1b4Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187117f1b4Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE.
237117f1b4Smrg *
247117f1b4Smrg * Authors:
25af69d88dSmrg *    Keith Whitwell <keithw@vmware.com>
267117f1b4Smrg */
277117f1b4Smrg
287117f1b4Smrg
2901e04c3fSmrg#include "c99_math.h"
3001e04c3fSmrg#include "main/errors.h"
31c1f859d4Smrg#include "main/glheader.h"
32c1f859d4Smrg#include "main/macros.h"
337ec681f3Smrg
34c1f859d4Smrg#include "main/mtypes.h"
357117f1b4Smrg
367117f1b4Smrg#include "math/m_xform.h"
377117f1b4Smrg
387117f1b4Smrg#include "t_context.h"
397117f1b4Smrg#include "t_pipeline.h"
407117f1b4Smrg
417117f1b4Smrg
427117f1b4Smrgstruct fog_stage_data {
437117f1b4Smrg   GLvector4f fogcoord;		/* has actual storage allocated */
447117f1b4Smrg};
457117f1b4Smrg
467117f1b4Smrg#define FOG_STAGE_DATA(stage) ((struct fog_stage_data *)stage->privatePtr)
477117f1b4Smrg
487117f1b4Smrg#define FOG_EXP_TABLE_SIZE 256
4901e04c3fSmrg#define FOG_MAX (10.0F)
5001e04c3fSmrg#define EXP_FOG_MAX .0006595F
517117f1b4Smrg#define FOG_INCR (FOG_MAX/FOG_EXP_TABLE_SIZE)
527117f1b4Smrgstatic GLfloat exp_table[FOG_EXP_TABLE_SIZE];
537117f1b4Smrgstatic GLfloat inited = 0;
547117f1b4Smrg
557117f1b4Smrg#if 1
567117f1b4Smrg#define NEG_EXP( result, narg )						\
577117f1b4Smrgdo {									\
5801e04c3fSmrg   GLfloat f = (GLfloat) (narg * (1.0F / FOG_INCR));			\
597117f1b4Smrg   GLint k = (GLint) f;							\
607117f1b4Smrg   if (k > FOG_EXP_TABLE_SIZE-2) 					\
617117f1b4Smrg      result = (GLfloat) EXP_FOG_MAX;					\
627117f1b4Smrg   else									\
637117f1b4Smrg      result = exp_table[k] + (f-k)*(exp_table[k+1]-exp_table[k]);	\
647117f1b4Smrg} while (0)
657117f1b4Smrg#else
667117f1b4Smrg#define NEG_EXP( result, narg )					\
677117f1b4Smrgdo {								\
687117f1b4Smrg   result = exp(-narg);						\
697117f1b4Smrg} while (0)
707117f1b4Smrg#endif
717117f1b4Smrg
727117f1b4Smrg
737117f1b4Smrg/**
747117f1b4Smrg * Initialize the exp_table[] lookup table for approximating exp().
757117f1b4Smrg */
767117f1b4Smrgstatic void
777117f1b4Smrginit_static_data( void )
787117f1b4Smrg{
797117f1b4Smrg   GLfloat f = 0.0F;
807117f1b4Smrg   GLint i = 0;
817117f1b4Smrg   for ( ; i < FOG_EXP_TABLE_SIZE ; i++, f += FOG_INCR) {
8201e04c3fSmrg      exp_table[i] = expf(-f);
837117f1b4Smrg   }
847117f1b4Smrg   inited = 1;
857117f1b4Smrg}
867117f1b4Smrg
877117f1b4Smrg
887117f1b4Smrg/**
897117f1b4Smrg * Compute per-vertex fog blend factors from fog coordinates by
907117f1b4Smrg * evaluating the GL_LINEAR, GL_EXP or GL_EXP2 fog function.
917117f1b4Smrg * Fog coordinates are distances from the eye (typically between the
927117f1b4Smrg * near and far clip plane distances).
937117f1b4Smrg * Note that fogcoords may be negative, if eye z is source absolute
947117f1b4Smrg * value must be taken earlier.
957117f1b4Smrg * Fog blend factors are in the range [0,1].
967117f1b4Smrg */
977117f1b4Smrgstatic void
983464ebd5Sriastradhcompute_fog_blend_factors(struct gl_context *ctx, GLvector4f *out, const GLvector4f *in)
997117f1b4Smrg{
1007117f1b4Smrg   GLfloat end  = ctx->Fog.End;
1017117f1b4Smrg   GLfloat *v = in->start;
1027117f1b4Smrg   GLuint stride = in->stride;
1037117f1b4Smrg   GLuint n = in->count;
1047117f1b4Smrg   GLfloat (*data)[4] = out->data;
1057117f1b4Smrg   GLfloat d;
1067117f1b4Smrg   GLuint i;
1077117f1b4Smrg
1087117f1b4Smrg   out->count = in->count;
1097117f1b4Smrg
1107117f1b4Smrg   switch (ctx->Fog.Mode) {
1117117f1b4Smrg   case GL_LINEAR:
1127117f1b4Smrg      if (ctx->Fog.Start == ctx->Fog.End)
1137117f1b4Smrg         d = 1.0F;
1147117f1b4Smrg      else
1157117f1b4Smrg         d = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
1167117f1b4Smrg      for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride)) {
1177117f1b4Smrg         const GLfloat z = *v;
1187117f1b4Smrg         GLfloat f = (end - z) * d;
1197117f1b4Smrg	 data[i][0] = CLAMP(f, 0.0F, 1.0F);
1207117f1b4Smrg      }
1217117f1b4Smrg      break;
1227117f1b4Smrg   case GL_EXP:
1237117f1b4Smrg      d = ctx->Fog.Density;
1247117f1b4Smrg      for ( i = 0 ; i < n ; i++, STRIDE_F(v,stride)) {
1257117f1b4Smrg         const GLfloat z = *v;
1267117f1b4Smrg         NEG_EXP( data[i][0], d * z );
1277117f1b4Smrg      }
1287117f1b4Smrg      break;
1297117f1b4Smrg   case GL_EXP2:
1307117f1b4Smrg      d = ctx->Fog.Density*ctx->Fog.Density;
1317117f1b4Smrg      for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride)) {
1327117f1b4Smrg         const GLfloat z = *v;
1337117f1b4Smrg         NEG_EXP( data[i][0], d * z * z );
1347117f1b4Smrg      }
1357117f1b4Smrg      break;
1367117f1b4Smrg   default:
1377117f1b4Smrg      _mesa_problem(ctx, "Bad fog mode in make_fog_coord");
1387117f1b4Smrg      return;
1397117f1b4Smrg   }
1407117f1b4Smrg}
1417117f1b4Smrg
1427117f1b4Smrg
1437117f1b4Smrgstatic GLboolean
1443464ebd5Sriastradhrun_fog_stage(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
1457117f1b4Smrg{
1467117f1b4Smrg   TNLcontext *tnl = TNL_CONTEXT(ctx);
1477117f1b4Smrg   struct vertex_buffer *VB = &tnl->vb;
1487117f1b4Smrg   struct fog_stage_data *store = FOG_STAGE_DATA(stage);
1497117f1b4Smrg   GLvector4f *input;
1507117f1b4Smrg
1517117f1b4Smrg
152c1f859d4Smrg   if (!ctx->Fog.Enabled)
153c1f859d4Smrg      return GL_TRUE;
1547117f1b4Smrg
155c1f859d4Smrg   if (ctx->Fog.FogCoordinateSource == GL_FRAGMENT_DEPTH_EXT && !ctx->VertexProgram._Current) {
1567117f1b4Smrg      GLuint i;
1577117f1b4Smrg      GLfloat *coord;
1587117f1b4Smrg      /* Fog is computed from vertex or fragment Z values */
159cdc920a0Smrg      /* source = VB->AttribPtr[_TNL_ATTRIB_POS] or VB->EyePtr coords */
1607117f1b4Smrg      /* dest = VB->AttribPtr[_TNL_ATTRIB_FOG] = fog stage private storage */
1617117f1b4Smrg      VB->AttribPtr[_TNL_ATTRIB_FOG] = &store->fogcoord;
1627117f1b4Smrg
1637117f1b4Smrg      if (!ctx->_NeedEyeCoords) {
1647117f1b4Smrg         /* compute fog coords from object coords */
1657117f1b4Smrg	 const GLfloat *m = ctx->ModelviewMatrixStack.Top->m;
1667117f1b4Smrg	 GLfloat plane[4];
1677117f1b4Smrg
1687117f1b4Smrg	 /* Use this to store calculated eye z values:
1697117f1b4Smrg	  */
1707117f1b4Smrg	 input = &store->fogcoord;
1717117f1b4Smrg
1727117f1b4Smrg	 plane[0] = m[2];
1737117f1b4Smrg	 plane[1] = m[6];
1747117f1b4Smrg	 plane[2] = m[10];
1757117f1b4Smrg	 plane[3] = m[14];
1767117f1b4Smrg	 /* Full eye coords weren't required, just calculate the
1777117f1b4Smrg	  * eye Z values.
1787117f1b4Smrg	  */
179cdc920a0Smrg	 _mesa_dotprod_tab[VB->AttribPtr[_TNL_ATTRIB_POS]->size]
180cdc920a0Smrg	    ( (GLfloat *) input->data,
181cdc920a0Smrg	      4 * sizeof(GLfloat),
182cdc920a0Smrg	      VB->AttribPtr[_TNL_ATTRIB_POS], plane );
1837117f1b4Smrg
184cdc920a0Smrg	 input->count = VB->AttribPtr[_TNL_ATTRIB_POS]->count;
1857117f1b4Smrg
1867117f1b4Smrg	 /* make sure coords are really positive
1877117f1b4Smrg	    NOTE should avoid going through array twice */
1887117f1b4Smrg	 coord = input->start;
1897117f1b4Smrg	 for (i = 0; i < input->count; i++) {
19001e04c3fSmrg	    *coord = fabsf(*coord);
1917117f1b4Smrg	    STRIDE_F(coord, input->stride);
1927117f1b4Smrg	 }
1937117f1b4Smrg      }
1947117f1b4Smrg      else {
1957117f1b4Smrg         /* fog coordinates = eye Z coordinates - need to copy for ABS */
1967117f1b4Smrg	 input = &store->fogcoord;
1977117f1b4Smrg
1987117f1b4Smrg	 if (VB->EyePtr->size < 2)
1997117f1b4Smrg	    _mesa_vector4f_clean_elem( VB->EyePtr, VB->Count, 2 );
2007117f1b4Smrg
2017117f1b4Smrg	 input->stride = 4 * sizeof(GLfloat);
2027117f1b4Smrg	 input->count = VB->EyePtr->count;
2037117f1b4Smrg	 coord = VB->EyePtr->start;
2047117f1b4Smrg	 for (i = 0 ; i < VB->EyePtr->count; i++) {
20501e04c3fSmrg	    input->data[i][0] = fabsf(coord[2]);
2067117f1b4Smrg	    STRIDE_F(coord, VB->EyePtr->stride);
2077117f1b4Smrg	 }
2087117f1b4Smrg      }
2097117f1b4Smrg   }
2107117f1b4Smrg   else {
2117117f1b4Smrg      /* use glFogCoord() coordinates */
2127117f1b4Smrg      input = VB->AttribPtr[_TNL_ATTRIB_FOG];  /* source data */
2137117f1b4Smrg
2147117f1b4Smrg      /* input->count may be one if glFogCoord was only called once
2157117f1b4Smrg       * before glBegin.  But we need to compute fog for all vertices.
2167117f1b4Smrg       */
217cdc920a0Smrg      input->count = VB->AttribPtr[_TNL_ATTRIB_POS]->count;
2187117f1b4Smrg
2197117f1b4Smrg      VB->AttribPtr[_TNL_ATTRIB_FOG] = &store->fogcoord;  /* dest data */
2207117f1b4Smrg   }
2217117f1b4Smrg
2227117f1b4Smrg   if (tnl->_DoVertexFog) {
2237117f1b4Smrg      /* compute blend factors from fog coordinates */
2247117f1b4Smrg      compute_fog_blend_factors( ctx, VB->AttribPtr[_TNL_ATTRIB_FOG], input );
2257117f1b4Smrg   }
2267117f1b4Smrg   else {
2277117f1b4Smrg      /* results = incoming fog coords (compute fog per-fragment later) */
2287117f1b4Smrg      VB->AttribPtr[_TNL_ATTRIB_FOG] = input;
2297117f1b4Smrg   }
2307117f1b4Smrg
2317117f1b4Smrg   return GL_TRUE;
2327117f1b4Smrg}
2337117f1b4Smrg
2347117f1b4Smrg
2357117f1b4Smrg
2367117f1b4Smrg/* Called the first time stage->run() is invoked.
2377117f1b4Smrg */
2387117f1b4Smrgstatic GLboolean
2393464ebd5Sriastradhalloc_fog_data(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
2407117f1b4Smrg{
2417117f1b4Smrg   TNLcontext *tnl = TNL_CONTEXT(ctx);
2427117f1b4Smrg   struct fog_stage_data *store;
243af69d88dSmrg   stage->privatePtr = malloc(sizeof(*store));
2447117f1b4Smrg   store = FOG_STAGE_DATA(stage);
2457117f1b4Smrg   if (!store)
2467117f1b4Smrg      return GL_FALSE;
2477117f1b4Smrg
2487117f1b4Smrg   _mesa_vector4f_alloc( &store->fogcoord, 0, tnl->vb.Size, 32 );
2497117f1b4Smrg
2507117f1b4Smrg   if (!inited)
2517117f1b4Smrg      init_static_data();
2527117f1b4Smrg
2537117f1b4Smrg   return GL_TRUE;
2547117f1b4Smrg}
2557117f1b4Smrg
2567117f1b4Smrg
2577117f1b4Smrgstatic void
2587117f1b4Smrgfree_fog_data(struct tnl_pipeline_stage *stage)
2597117f1b4Smrg{
2607117f1b4Smrg   struct fog_stage_data *store = FOG_STAGE_DATA(stage);
2617117f1b4Smrg   if (store) {
2627117f1b4Smrg      _mesa_vector4f_free( &store->fogcoord );
263af69d88dSmrg      free( store );
2647117f1b4Smrg      stage->privatePtr = NULL;
2657117f1b4Smrg   }
2667117f1b4Smrg}
2677117f1b4Smrg
2687117f1b4Smrg
2697117f1b4Smrgconst struct tnl_pipeline_stage _tnl_fog_coordinate_stage =
2707117f1b4Smrg{
2717117f1b4Smrg   "build fog coordinates",	/* name */
2727117f1b4Smrg   NULL,			/* private_data */
2737117f1b4Smrg   alloc_fog_data,		/* dtr */
2747117f1b4Smrg   free_fog_data,		/* dtr */
2757117f1b4Smrg   NULL,		/* check */
2767117f1b4Smrg   run_fog_stage		/* run -- initially set to init. */
2777117f1b4Smrg};
278