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