1 /*
2  * Mesa 3-D graphics library
3  *
4  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 
26 #include "c99_math.h"
27 #include "main/errors.h"
28 #include "main/glheader.h"
29 #include "main/macros.h"
30 
31 #include "s_context.h"
32 #include "s_fog.h"
33 
34 
35 /**
36  * Used to convert current raster distance to a fog factor in [0,1].
37  */
38 GLfloat
_swrast_z_to_fogfactor(struct gl_context * ctx,GLfloat z)39 _swrast_z_to_fogfactor(struct gl_context *ctx, GLfloat z)
40 {
41    GLfloat d, f;
42 
43    switch (ctx->Fog.Mode) {
44    case GL_LINEAR:
45       if (ctx->Fog.Start == ctx->Fog.End)
46          d = 1.0F;
47       else
48          d = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
49       f = (ctx->Fog.End - z) * d;
50       return CLAMP(f, 0.0F, 1.0F);
51    case GL_EXP:
52       d = ctx->Fog.Density;
53       f = expf(-d * z);
54       f = CLAMP(f, 0.0F, 1.0F);
55       return f;
56    case GL_EXP2:
57       d = ctx->Fog.Density;
58       f = expf(-(d * d * z * z));
59       f = CLAMP(f, 0.0F, 1.0F);
60       return f;
61    default:
62       _mesa_problem(ctx, "Bad fog mode in _swrast_z_to_fogfactor");
63       return 0.0;
64    }
65 }
66 
67 
68 #define LINEAR_FOG(f, coord)  f = (fogEnd - coord) * fogScale
69 
70 #define EXP_FOG(f, coord)  f = expf(density * coord)
71 
72 #define EXP2_FOG(f, coord)				\
73 do {							\
74    GLfloat tmp = negDensitySquared * coord * coord;	\
75    if (tmp < FLT_MIN_10_EXP)				\
76       tmp = FLT_MIN_10_EXP;				\
77    f = expf(tmp);					\
78  } while(0)
79 
80 
81 #define BLEND_FOG(f, coord)  f = coord
82 
83 
84 
85 /**
86  * Template code for computing fog blend factor and applying it to colors.
87  * \param TYPE  either GLubyte, GLushort or GLfloat.
88  * \param COMPUTE_F  code to compute the fog blend factor, f.
89  */
90 #define FOG_LOOP(TYPE, FOG_FUNC)						\
91 if (span->arrayAttribs & VARYING_BIT_FOGC) {					\
92    GLuint i;									\
93    for (i = 0; i < span->end; i++) {						\
94       const GLfloat fogCoord = span->array->attribs[VARYING_SLOT_FOGC][i][0];	\
95       const GLfloat c = fabsf(fogCoord);					\
96       GLfloat f, oneMinusF;							\
97       FOG_FUNC(f, c);								\
98       f = CLAMP(f, 0.0F, 1.0F);							\
99       oneMinusF = 1.0F - f;							\
100       rgba[i][RCOMP] = (TYPE) (f * rgba[i][RCOMP] + oneMinusF * rFog);		\
101       rgba[i][GCOMP] = (TYPE) (f * rgba[i][GCOMP] + oneMinusF * gFog);		\
102       rgba[i][BCOMP] = (TYPE) (f * rgba[i][BCOMP] + oneMinusF * bFog);		\
103    }										\
104 }										\
105 else {										\
106    const GLfloat fogStep = span->attrStepX[VARYING_SLOT_FOGC][0];		\
107    GLfloat fogCoord = span->attrStart[VARYING_SLOT_FOGC][0];			\
108    const GLfloat wStep = span->attrStepX[VARYING_SLOT_POS][3];			\
109    GLfloat w = span->attrStart[VARYING_SLOT_POS][3];				\
110    GLuint i;									\
111    for (i = 0; i < span->end; i++) {						\
112       const GLfloat c = fabsf(fogCoord) / w;					\
113       GLfloat f, oneMinusF;							\
114       FOG_FUNC(f, c);								\
115       f = CLAMP(f, 0.0F, 1.0F);							\
116       oneMinusF = 1.0F - f;							\
117       rgba[i][RCOMP] = (TYPE) (f * rgba[i][RCOMP] + oneMinusF * rFog);		\
118       rgba[i][GCOMP] = (TYPE) (f * rgba[i][GCOMP] + oneMinusF * gFog);		\
119       rgba[i][BCOMP] = (TYPE) (f * rgba[i][BCOMP] + oneMinusF * bFog);		\
120       fogCoord += fogStep;							\
121       w += wStep;								\
122    }										\
123 }
124 
125 /**
126  * Apply fog to a span of RGBA pixels.
127  * The fog value are either in the span->array->fog array or interpolated from
128  * the fog/fogStep values.
129  * They fog values are either fog coordinates (Z) or fog blend factors.
130  * _PreferPixelFog should be in sync with that state!
131  */
132 void
_swrast_fog_rgba_span(const struct gl_context * ctx,SWspan * span)133 _swrast_fog_rgba_span( const struct gl_context *ctx, SWspan *span )
134 {
135    const SWcontext *swrast = CONST_SWRAST_CONTEXT(ctx);
136    GLfloat rFog, gFog, bFog;
137 
138    assert(swrast->_FogEnabled);
139    assert(span->arrayMask & SPAN_RGBA);
140 
141    /* compute (scaled) fog color */
142    if (span->array->ChanType == GL_UNSIGNED_BYTE) {
143       rFog = ctx->Fog.Color[RCOMP] * 255.0F;
144       gFog = ctx->Fog.Color[GCOMP] * 255.0F;
145       bFog = ctx->Fog.Color[BCOMP] * 255.0F;
146    }
147    else if (span->array->ChanType == GL_UNSIGNED_SHORT) {
148       rFog = ctx->Fog.Color[RCOMP] * 65535.0F;
149       gFog = ctx->Fog.Color[GCOMP] * 65535.0F;
150       bFog = ctx->Fog.Color[BCOMP] * 65535.0F;
151    }
152    else {
153       rFog = ctx->Fog.Color[RCOMP];
154       gFog = ctx->Fog.Color[GCOMP];
155       bFog = ctx->Fog.Color[BCOMP];
156    }
157 
158    if (swrast->_PreferPixelFog) {
159       /* The span's fog values are fog coordinates, now compute blend factors
160        * and blend the fragment colors with the fog color.
161        */
162       switch (ctx->Fog.Mode) {
163       case GL_LINEAR:
164          {
165             const GLfloat fogEnd = ctx->Fog.End;
166             const GLfloat fogScale = (ctx->Fog.Start == ctx->Fog.End)
167                ? 1.0F : 1.0F / (ctx->Fog.End - ctx->Fog.Start);
168             if (span->array->ChanType == GL_UNSIGNED_BYTE) {
169                GLubyte (*rgba)[4] = span->array->rgba8;
170                FOG_LOOP(GLubyte, LINEAR_FOG);
171             }
172             else if (span->array->ChanType == GL_UNSIGNED_SHORT) {
173                GLushort (*rgba)[4] = span->array->rgba16;
174                FOG_LOOP(GLushort, LINEAR_FOG);
175             }
176             else {
177                GLfloat (*rgba)[4] = span->array->attribs[VARYING_SLOT_COL0];
178                assert(span->array->ChanType == GL_FLOAT);
179                FOG_LOOP(GLfloat, LINEAR_FOG);
180             }
181          }
182          break;
183 
184       case GL_EXP:
185          {
186             const GLfloat density = -ctx->Fog.Density;
187             if (span->array->ChanType == GL_UNSIGNED_BYTE) {
188                GLubyte (*rgba)[4] = span->array->rgba8;
189                FOG_LOOP(GLubyte, EXP_FOG);
190             }
191             else if (span->array->ChanType == GL_UNSIGNED_SHORT) {
192                GLushort (*rgba)[4] = span->array->rgba16;
193                FOG_LOOP(GLushort, EXP_FOG);
194             }
195             else {
196                GLfloat (*rgba)[4] = span->array->attribs[VARYING_SLOT_COL0];
197                assert(span->array->ChanType == GL_FLOAT);
198                FOG_LOOP(GLfloat, EXP_FOG);
199             }
200          }
201          break;
202 
203       case GL_EXP2:
204          {
205             const GLfloat negDensitySquared = -ctx->Fog.Density * ctx->Fog.Density;
206             if (span->array->ChanType == GL_UNSIGNED_BYTE) {
207                GLubyte (*rgba)[4] = span->array->rgba8;
208                FOG_LOOP(GLubyte, EXP2_FOG);
209             }
210             else if (span->array->ChanType == GL_UNSIGNED_SHORT) {
211                GLushort (*rgba)[4] = span->array->rgba16;
212                FOG_LOOP(GLushort, EXP2_FOG);
213             }
214             else {
215                GLfloat (*rgba)[4] = span->array->attribs[VARYING_SLOT_COL0];
216                assert(span->array->ChanType == GL_FLOAT);
217                FOG_LOOP(GLfloat, EXP2_FOG);
218             }
219          }
220          break;
221 
222       default:
223          _mesa_problem(ctx, "Bad fog mode in _swrast_fog_rgba_span");
224          return;
225       }
226    }
227    else {
228       /* The span's fog start/step/array values are blend factors in [0,1].
229        * They were previously computed per-vertex.
230        */
231       if (span->array->ChanType == GL_UNSIGNED_BYTE) {
232          GLubyte (*rgba)[4] = span->array->rgba8;
233          FOG_LOOP(GLubyte, BLEND_FOG);
234       }
235       else if (span->array->ChanType == GL_UNSIGNED_SHORT) {
236          GLushort (*rgba)[4] = span->array->rgba16;
237          FOG_LOOP(GLushort, BLEND_FOG);
238       }
239       else {
240          GLfloat (*rgba)[4] = span->array->attribs[VARYING_SLOT_COL0];
241          assert(span->array->ChanType == GL_FLOAT);
242          FOG_LOOP(GLfloat, BLEND_FOG);
243       }
244    }
245 }
246