1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5
4 *
5 * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 *    Keith Whitwell <keith@tungstengraphics.com>
26 */
27
28
29#include "main/glheader.h"
30#include "main/colormac.h"
31#include "main/macros.h"
32#include "main/imports.h"
33#include "main/mtypes.h"
34
35#include "math/m_xform.h"
36
37#include "t_context.h"
38#include "t_pipeline.h"
39
40
41struct fog_stage_data {
42   GLvector4f fogcoord;		/* has actual storage allocated */
43};
44
45#define FOG_STAGE_DATA(stage) ((struct fog_stage_data *)stage->privatePtr)
46
47#define FOG_EXP_TABLE_SIZE 256
48#define FOG_MAX (10.0)
49#define EXP_FOG_MAX .0006595
50#define FOG_INCR (FOG_MAX/FOG_EXP_TABLE_SIZE)
51static GLfloat exp_table[FOG_EXP_TABLE_SIZE];
52static GLfloat inited = 0;
53
54#if 1
55#define NEG_EXP( result, narg )						\
56do {									\
57   GLfloat f = (GLfloat) (narg * (1.0/FOG_INCR));			\
58   GLint k = (GLint) f;							\
59   if (k > FOG_EXP_TABLE_SIZE-2) 					\
60      result = (GLfloat) EXP_FOG_MAX;					\
61   else									\
62      result = exp_table[k] + (f-k)*(exp_table[k+1]-exp_table[k]);	\
63} while (0)
64#else
65#define NEG_EXP( result, narg )					\
66do {								\
67   result = exp(-narg);						\
68} while (0)
69#endif
70
71
72/**
73 * Initialize the exp_table[] lookup table for approximating exp().
74 */
75static void
76init_static_data( void )
77{
78   GLfloat f = 0.0F;
79   GLint i = 0;
80   for ( ; i < FOG_EXP_TABLE_SIZE ; i++, f += FOG_INCR) {
81      exp_table[i] = EXPF(-f);
82   }
83   inited = 1;
84}
85
86
87/**
88 * Compute per-vertex fog blend factors from fog coordinates by
89 * evaluating the GL_LINEAR, GL_EXP or GL_EXP2 fog function.
90 * Fog coordinates are distances from the eye (typically between the
91 * near and far clip plane distances).
92 * Note that fogcoords may be negative, if eye z is source absolute
93 * value must be taken earlier.
94 * Fog blend factors are in the range [0,1].
95 */
96static void
97compute_fog_blend_factors(struct gl_context *ctx, GLvector4f *out, const GLvector4f *in)
98{
99   GLfloat end  = ctx->Fog.End;
100   GLfloat *v = in->start;
101   GLuint stride = in->stride;
102   GLuint n = in->count;
103   GLfloat (*data)[4] = out->data;
104   GLfloat d;
105   GLuint i;
106
107   out->count = in->count;
108
109   switch (ctx->Fog.Mode) {
110   case GL_LINEAR:
111      if (ctx->Fog.Start == ctx->Fog.End)
112         d = 1.0F;
113      else
114         d = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
115      for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride)) {
116         const GLfloat z = *v;
117         GLfloat f = (end - z) * d;
118	 data[i][0] = CLAMP(f, 0.0F, 1.0F);
119      }
120      break;
121   case GL_EXP:
122      d = ctx->Fog.Density;
123      for ( i = 0 ; i < n ; i++, STRIDE_F(v,stride)) {
124         const GLfloat z = *v;
125         NEG_EXP( data[i][0], d * z );
126      }
127      break;
128   case GL_EXP2:
129      d = ctx->Fog.Density*ctx->Fog.Density;
130      for ( i = 0 ; i < n ; i++, STRIDE_F(v, stride)) {
131         const GLfloat z = *v;
132         NEG_EXP( data[i][0], d * z * z );
133      }
134      break;
135   default:
136      _mesa_problem(ctx, "Bad fog mode in make_fog_coord");
137      return;
138   }
139}
140
141
142static GLboolean
143run_fog_stage(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
144{
145   TNLcontext *tnl = TNL_CONTEXT(ctx);
146   struct vertex_buffer *VB = &tnl->vb;
147   struct fog_stage_data *store = FOG_STAGE_DATA(stage);
148   GLvector4f *input;
149
150
151   if (!ctx->Fog.Enabled)
152      return GL_TRUE;
153
154   if (ctx->Fog.FogCoordinateSource == GL_FRAGMENT_DEPTH_EXT && !ctx->VertexProgram._Current) {
155      GLuint i;
156      GLfloat *coord;
157      /* Fog is computed from vertex or fragment Z values */
158      /* source = VB->AttribPtr[_TNL_ATTRIB_POS] or VB->EyePtr coords */
159      /* dest = VB->AttribPtr[_TNL_ATTRIB_FOG] = fog stage private storage */
160      VB->AttribPtr[_TNL_ATTRIB_FOG] = &store->fogcoord;
161
162      if (!ctx->_NeedEyeCoords) {
163         /* compute fog coords from object coords */
164	 const GLfloat *m = ctx->ModelviewMatrixStack.Top->m;
165	 GLfloat plane[4];
166
167	 /* Use this to store calculated eye z values:
168	  */
169	 input = &store->fogcoord;
170
171	 plane[0] = m[2];
172	 plane[1] = m[6];
173	 plane[2] = m[10];
174	 plane[3] = m[14];
175	 /* Full eye coords weren't required, just calculate the
176	  * eye Z values.
177	  */
178	 _mesa_dotprod_tab[VB->AttribPtr[_TNL_ATTRIB_POS]->size]
179	    ( (GLfloat *) input->data,
180	      4 * sizeof(GLfloat),
181	      VB->AttribPtr[_TNL_ATTRIB_POS], plane );
182
183	 input->count = VB->AttribPtr[_TNL_ATTRIB_POS]->count;
184
185	 /* make sure coords are really positive
186	    NOTE should avoid going through array twice */
187	 coord = input->start;
188	 for (i = 0; i < input->count; i++) {
189	    *coord = FABSF(*coord);
190	    STRIDE_F(coord, input->stride);
191	 }
192      }
193      else {
194         /* fog coordinates = eye Z coordinates - need to copy for ABS */
195	 input = &store->fogcoord;
196
197	 if (VB->EyePtr->size < 2)
198	    _mesa_vector4f_clean_elem( VB->EyePtr, VB->Count, 2 );
199
200	 input->stride = 4 * sizeof(GLfloat);
201	 input->count = VB->EyePtr->count;
202	 coord = VB->EyePtr->start;
203	 for (i = 0 ; i < VB->EyePtr->count; i++) {
204	    input->data[i][0] = FABSF(coord[2]);
205	    STRIDE_F(coord, VB->EyePtr->stride);
206	 }
207      }
208   }
209   else {
210      /* use glFogCoord() coordinates */
211      input = VB->AttribPtr[_TNL_ATTRIB_FOG];  /* source data */
212
213      /* input->count may be one if glFogCoord was only called once
214       * before glBegin.  But we need to compute fog for all vertices.
215       */
216      input->count = VB->AttribPtr[_TNL_ATTRIB_POS]->count;
217
218      VB->AttribPtr[_TNL_ATTRIB_FOG] = &store->fogcoord;  /* dest data */
219   }
220
221   if (tnl->_DoVertexFog) {
222      /* compute blend factors from fog coordinates */
223      compute_fog_blend_factors( ctx, VB->AttribPtr[_TNL_ATTRIB_FOG], input );
224   }
225   else {
226      /* results = incoming fog coords (compute fog per-fragment later) */
227      VB->AttribPtr[_TNL_ATTRIB_FOG] = input;
228   }
229
230   return GL_TRUE;
231}
232
233
234
235/* Called the first time stage->run() is invoked.
236 */
237static GLboolean
238alloc_fog_data(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
239{
240   TNLcontext *tnl = TNL_CONTEXT(ctx);
241   struct fog_stage_data *store;
242   stage->privatePtr = MALLOC(sizeof(*store));
243   store = FOG_STAGE_DATA(stage);
244   if (!store)
245      return GL_FALSE;
246
247   _mesa_vector4f_alloc( &store->fogcoord, 0, tnl->vb.Size, 32 );
248
249   if (!inited)
250      init_static_data();
251
252   return GL_TRUE;
253}
254
255
256static void
257free_fog_data(struct tnl_pipeline_stage *stage)
258{
259   struct fog_stage_data *store = FOG_STAGE_DATA(stage);
260   if (store) {
261      _mesa_vector4f_free( &store->fogcoord );
262      FREE( store );
263      stage->privatePtr = NULL;
264   }
265}
266
267
268const struct tnl_pipeline_stage _tnl_fog_coordinate_stage =
269{
270   "build fog coordinates",	/* name */
271   NULL,			/* private_data */
272   alloc_fog_data,		/* dtr */
273   free_fog_data,		/* dtr */
274   NULL,		/* check */
275   run_fog_stage		/* run -- initially set to init. */
276};
277