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