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 (at) 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 41 struct 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) 51 static GLfloat exp_table[FOG_EXP_TABLE_SIZE]; 52 static GLfloat inited = 0; 53 54 #if 1 55 #define NEG_EXP( result, narg ) \ 56 do { \ 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 ) \ 66 do { \ 67 result = exp(-narg); \ 68 } while (0) 69 #endif 70 71 72 /** 73 * Initialize the exp_table[] lookup table for approximating exp(). 74 */ 75 static void 76 init_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 */ 96 static void 97 compute_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 142 static GLboolean 143 run_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 */ 237 static GLboolean 238 alloc_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 256 static void 257 free_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 268 const 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