xref: /reactos/dll/opengl/mesa/shade.c (revision 5f2bebf7)
1 /* $Id: shade.c,v 1.10 1997/12/18 02:54:48 brianp Exp $ */
2 
3 /*
4  * Mesa 3-D graphics library
5  * Version:  2.6
6  * Copyright (C) 1995-1997  Brian Paul
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the Free
20  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 
23 
24 /*
25  * $Log: shade.c,v $
26  * Revision 1.10  1997/12/18 02:54:48  brianp
27  * now using FloatToInt() macro for better performance on x86
28  *
29  * Revision 1.9  1997/07/24 01:21:56  brianp
30  * changed precompiled header symbol from PCH to PC_HEADER
31  *
32  * Revision 1.8  1997/07/09 03:04:44  brianp
33  * fixed bug in gl_color_shade_vertices() with GL_COLOR_MATERIAL
34  *
35  * Revision 1.7  1997/07/05 16:24:26  brianp
36  * fixed FP underflow problem in pow().  Renamed h[xyz] to h_[xyz].
37  *
38  * Revision 1.6  1997/06/20 04:15:43  brianp
39  * optimized changing of SHININESS (Henk Kok)
40  *
41  * Revision 1.5  1997/06/20 02:28:40  brianp
42  * changed color components from GLfixed to GLubyte
43  *
44  * Revision 1.4  1997/05/28 03:26:29  brianp
45  * added precompiled header (PCH) support
46  *
47  * Revision 1.3  1997/05/23 03:01:18  brianp
48  * commented out a few const keywords because IRIX cc chokes on them
49  *
50  * Revision 1.2  1997/05/09 02:41:08  brianp
51  * call GL_SQRT() instead of sqrt()
52  *
53  * Revision 1.1  1997/04/01 04:11:04  brianp
54  * Initial revision
55  *
56  */
57 
58 
59 #ifdef PC_HEADER
60 #include "all.h"
61 #else
62 #include <math.h>
63 #include "macros.h"
64 #include "mmath.h"
65 #include "shade.h"
66 #include "types.h"
67 #endif
68 
69 
70 
71 /*
72  * Return x^y.
73  */
gl_pow(GLfloat x,GLfloat y)74 static GLfloat gl_pow( GLfloat x, GLfloat y )
75 {
76    GLdouble z = pow(x, y);
77    if (z<1.0e-10)
78       return 0.0F;
79    else
80       return (GLfloat) z;
81 }
82 
83 
84 
85 /*
86  * Use current lighting/material settings to compute the RGBA colors of
87  * an array of vertexes.
88  * Input:  side - 0=use front material, 1=use back material
89  *         n - number of vertexes to process
90  *         vertex - array of vertex positions in eye coordinates
91  *         normal - array of surface normal vectors
92  * Output:  color - array of resulting colors
93  */
gl_color_shade_vertices(GLcontext * ctx,GLuint side,GLuint n,GLfloat vertex[][4],GLfloat normal[][3],GLubyte color[][4])94 void gl_color_shade_vertices( GLcontext *ctx,
95                               GLuint side,
96                               GLuint n,
97                               /*const*/ GLfloat vertex[][4],
98                               /*const*/ GLfloat normal[][3],
99                               GLubyte color[][4] )
100 {
101    GLint j;
102    GLfloat rscale, gscale, bscale, ascale;
103    GLfloat baseR, baseG, baseB, baseA;
104    GLint sumA;
105    struct gl_light *light;
106    struct gl_material *mat;
107 
108    /* Compute scale factor to go from floats in [0,1] to integers or fixed
109     * point values:
110     */
111    rscale = ctx->Visual->RedScale;
112    gscale = ctx->Visual->GreenScale;
113    bscale = ctx->Visual->BlueScale;
114    ascale = ctx->Visual->AlphaScale;
115 
116    mat = &ctx->Light.Material[side];
117 
118    /*** Compute color contribution from global lighting ***/
119    baseR = mat->Emission[0] + ctx->Light.Model.Ambient[0] * mat->Ambient[0];
120    baseG = mat->Emission[1] + ctx->Light.Model.Ambient[1] * mat->Ambient[1];
121    baseB = mat->Emission[2] + ctx->Light.Model.Ambient[2] * mat->Ambient[2];
122    baseA = mat->Diffuse[3];  /* Alpha is simple, same for all vertices */
123 
124    sumA = (GLint) (CLAMP( baseA, 0.0F, 1.0F ) * ascale);
125 
126    for (j=0;j<n;j++) {
127       GLfloat sumR, sumG, sumB;
128       GLfloat nx, ny, nz;
129 
130       if (side==0) {
131          /* shade frontside */
132          nx = normal[j][0];
133          ny = normal[j][1];
134          nz = normal[j][2];
135       }
136       else {
137          /* shade backside */
138          nx = -normal[j][0];
139          ny = -normal[j][1];
140          nz = -normal[j][2];
141       }
142 
143       sumR = baseR;
144       sumG = baseG;
145       sumB = baseB;
146 
147       /* Add contribution from each enabled light source */
148       for (light=ctx->Light.FirstEnabled; light; light=light->NextEnabled) {
149          GLfloat ambientR, ambientG, ambientB;
150          GLfloat attenuation, spot;
151          GLfloat VPx, VPy, VPz;  /* unit vector from vertex to light */
152          GLfloat n_dot_VP;       /* n dot VP */
153 
154          /* compute VP and attenuation */
155          if (light->Position[3]==0.0) {
156             /* directional light */
157             VPx = light->VP_inf_norm[0];
158             VPy = light->VP_inf_norm[1];
159             VPz = light->VP_inf_norm[2];
160             attenuation = 1.0F;
161          }
162          else {
163             /* positional light */
164             GLfloat d;     /* distance from vertex to light */
165             VPx = light->Position[0] - vertex[j][0];
166             VPy = light->Position[1] - vertex[j][1];
167             VPz = light->Position[2] - vertex[j][2];
168             d = (GLfloat) GL_SQRT( VPx*VPx + VPy*VPy + VPz*VPz );
169             if (d>0.001F) {
170                GLfloat invd = 1.0F / d;
171                VPx *= invd;
172                VPy *= invd;
173                VPz *= invd;
174             }
175             attenuation = 1.0F / (light->ConstantAttenuation
176                         + d * (light->LinearAttenuation
177                         + d * light->QuadraticAttenuation));
178          }
179 
180          /* spotlight factor */
181          if (light->SpotCutoff==180.0F) {
182             /* not a spot light */
183             spot = 1.0F;
184          }
185          else {
186             GLfloat PVx, PVy, PVz, PV_dot_dir;
187             PVx = -VPx;
188             PVy = -VPy;
189             PVz = -VPz;
190             PV_dot_dir = PVx*light->NormDirection[0]
191                        + PVy*light->NormDirection[1]
192                        + PVz*light->NormDirection[2];
193             if (PV_dot_dir<=0.0F || PV_dot_dir<light->CosCutoff) {
194                /* outside of cone */
195                spot = 0.0F;
196             }
197             else {
198                double x = PV_dot_dir * (EXP_TABLE_SIZE-1);
199                int k = (int) x;
200                spot = light->SpotExpTable[k][0]
201                     + (x-k)*light->SpotExpTable[k][1];
202             }
203          }
204 
205          ambientR = mat->Ambient[0] * light->Ambient[0];
206          ambientG = mat->Ambient[1] * light->Ambient[1];
207          ambientB = mat->Ambient[2] * light->Ambient[2];
208 
209          /* Compute dot product or normal and vector from V to light pos */
210          n_dot_VP = nx * VPx + ny * VPy + nz * VPz;
211 
212          /* diffuse and specular terms */
213          if (n_dot_VP<=0.0F) {
214             /* surface face away from light, no diffuse or specular */
215             GLfloat t = attenuation * spot;
216             sumR += t * ambientR;
217             sumG += t * ambientG;
218             sumB += t * ambientB;
219             /* done with this light */
220          }
221          else {
222             GLfloat diffuseR, diffuseG, diffuseB;
223             GLfloat specularR, specularG, specularB;
224             GLfloat h_x, h_y, h_z, n_dot_h, t;
225 
226             /* diffuse term */
227             diffuseR = n_dot_VP * mat->Diffuse[0] * light->Diffuse[0];
228             diffuseG = n_dot_VP * mat->Diffuse[1] * light->Diffuse[1];
229             diffuseB = n_dot_VP * mat->Diffuse[2] * light->Diffuse[2];
230 
231             /* specular term */
232             if (ctx->Light.Model.LocalViewer) {
233                GLfloat vx, vy, vz, vlen;
234                vx = vertex[j][0];
235                vy = vertex[j][1];
236                vz = vertex[j][2];
237                vlen = GL_SQRT( vx*vx + vy*vy + vz*vz );
238                if (vlen>0.0001F) {
239                   GLfloat invlen = 1.0F / vlen;
240                   vx *= invlen;
241                   vy *= invlen;
242                   vz *= invlen;
243                }
244                /* h = VP + VPe */
245                h_x = VPx - vx;
246                h_y = VPy - vy;
247                h_z = VPz - vz;
248             }
249             else {
250                /* h = VP + <0,0,1> */
251                h_x = VPx;
252                h_y = VPy;
253                h_z = VPz + 1.0F;
254             }
255 
256             /* attention: h is not normalized, done later if needed */
257             n_dot_h = nx*h_x + ny*h_y + nz*h_z;
258 
259             if (n_dot_h<=0.0F) {
260                specularR = 0.0F;
261                specularG = 0.0F;
262                specularB = 0.0F;
263             }
264             else {
265                GLfloat spec_coef;
266                /* now `correct' the dot product */
267                n_dot_h = n_dot_h / GL_SQRT( h_x*h_x + h_y*h_y + h_z*h_z );
268                if (n_dot_h>1.0F) {
269                   /* only happens if normal vector length > 1.0 */
270                   spec_coef = pow( n_dot_h, mat->Shininess );
271                }
272                else {
273                   /* use table lookup approximation */
274                   int k = (int) (n_dot_h * (GLfloat) (SHINE_TABLE_SIZE-1));
275                   if (mat->ShineTable[k] < 0.0F)
276                      mat->ShineTable[k] = gl_pow( n_dot_h, mat->Shininess );
277                   spec_coef = mat->ShineTable[k];
278                }
279                if (spec_coef<1.0e-10) {
280                   specularR = 0.0F;
281                   specularG = 0.0F;
282                   specularB = 0.0F;
283                }
284                else {
285                   specularR = spec_coef * mat->Specular[0]*light->Specular[0];
286                   specularG = spec_coef * mat->Specular[1]*light->Specular[1];
287                   specularB = spec_coef * mat->Specular[2]*light->Specular[2];
288                }
289             }
290 
291             t = attenuation * spot;
292             sumR += t * (ambientR + diffuseR + specularR);
293             sumG += t * (ambientG + diffuseG + specularG);
294             sumB += t * (ambientB + diffuseB + specularB);
295          }
296 
297       } /*loop over lights*/
298 
299       /* clamp and convert to integer or fixed point */
300       color[j][0] = FloatToInt(CLAMP( sumR, 0.0F, 1.0F ) * rscale);
301       color[j][1] = FloatToInt(CLAMP( sumG, 0.0F, 1.0F ) * gscale);
302       color[j][2] = FloatToInt(CLAMP( sumB, 0.0F, 1.0F ) * bscale);
303       color[j][3] = sumA;
304 
305    } /*loop over vertices*/
306 }
307 
308 
309 
310 /*
311  * This is an optimized version of the above function.
312  */
gl_color_shade_vertices_fast(GLcontext * ctx,GLuint side,GLuint n,GLfloat normal[][3],GLubyte color[][4])313 void gl_color_shade_vertices_fast( GLcontext *ctx,
314                                    GLuint side,
315                                    GLuint n,
316                                    /*const*/ GLfloat normal[][3],
317                                    GLubyte color[][4] )
318 {
319    GLint j;
320    GLfloat rscale, gscale, bscale, ascale;
321    GLint sumA;
322    GLfloat *baseColor = ctx->Light.BaseColor[side];
323 
324    /* Compute scale factor to go from floats in [0,1] to integers or fixed
325     * point values:
326     */
327    rscale = ctx->Visual->RedScale;
328    gscale = ctx->Visual->GreenScale;
329    bscale = ctx->Visual->BlueScale;
330    ascale = ctx->Visual->AlphaScale;
331 
332    /* Alpha is easy to compute, same for all vertices */
333    sumA = (GLint) (baseColor[3] * ascale);
334 
335    /* Loop over vertices */
336    for (j=0;j<n;j++) {
337       GLfloat sumR, sumG, sumB;
338       GLfloat nx, ny, nz;
339       struct gl_light *light;
340 
341       /* the normal vector */
342       if (side==0) {
343          nx = normal[j][0];
344          ny = normal[j][1];
345          nz = normal[j][2];
346       }
347       else {
348          nx = -normal[j][0];
349          ny = -normal[j][1];
350          nz = -normal[j][2];
351       }
352 
353 #ifdef SPEED_HACK
354       if (nz<0.0F) {
355          color[j][0] = 0.0F;
356          color[j][1] = 0.0F;
357          color[j][2] = 0.0F;
358          color[j][3] = A;
359          continue;
360       }
361 #endif
362 
363       /* base color from global illumination and enabled light's ambient */
364       sumR = baseColor[0];
365       sumG = baseColor[1];
366       sumB = baseColor[2];
367 
368       /* Add contribution from each light source */
369       for (light=ctx->Light.FirstEnabled; light; light=light->NextEnabled) {
370          GLfloat n_dot_VP;     /* n dot VP */
371 
372          n_dot_VP = nx * light->VP_inf_norm[0]
373                   + ny * light->VP_inf_norm[1]
374                   + nz * light->VP_inf_norm[2];
375 
376          /* diffuse and specular terms */
377          if (n_dot_VP>0.0F) {
378             GLfloat n_dot_h;
379             GLfloat *lightMatDiffuse = light->MatDiffuse[side];
380 
381             /** add diffuse term **/
382             sumR += n_dot_VP * lightMatDiffuse[0];
383             sumG += n_dot_VP * lightMatDiffuse[1];
384             sumB += n_dot_VP * lightMatDiffuse[2];
385 
386             /** specular term **/
387             /* dot product of n and h_inf_norm */
388             n_dot_h = nx * light->h_inf_norm[0]
389                     + ny * light->h_inf_norm[1]
390                     + nz * light->h_inf_norm[2];
391             if (n_dot_h>0.0F) {
392                if (n_dot_h>1.0F) {
393                   /* only happens if Magnitude(n) > 1.0 */
394                   GLfloat spec_coef = pow( n_dot_h,
395                                         ctx->Light.Material[side].Shininess );
396                   if (spec_coef>1.0e-10F) {
397                      sumR += spec_coef * light->MatSpecular[side][0];
398                      sumG += spec_coef * light->MatSpecular[side][1];
399                      sumB += spec_coef * light->MatSpecular[side][2];
400                   }
401                }
402                else {
403                   /* use table lookup approximation */
404                   int k = (int) (n_dot_h * (GLfloat) (SHINE_TABLE_SIZE-1));
405                   struct gl_material *m = &ctx->Light.Material[side];
406                   GLfloat spec_coef;
407                   if (m->ShineTable[k] < 0.0F)
408                      m->ShineTable[k] = gl_pow( n_dot_h, m->Shininess );
409                   spec_coef = m->ShineTable[k];
410                   sumR += spec_coef * light->MatSpecular[side][0];
411                   sumG += spec_coef * light->MatSpecular[side][1];
412                   sumB += spec_coef * light->MatSpecular[side][2];
413                }
414             }
415          }
416 
417       } /*loop over lights*/
418 
419       /* clamp and convert to integer or fixed point */
420       color[j][0] = FloatToInt(MIN2( sumR, 1.0F ) * rscale);
421       color[j][1] = FloatToInt(MIN2( sumG, 1.0F ) * gscale);
422       color[j][2] = FloatToInt(MIN2( sumB, 1.0F ) * bscale);
423       color[j][3] = sumA;
424 
425    } /*loop over vertices*/
426 }
427 
428 
429 
430 /*
431  * Use current lighting/material settings to compute the color indexes
432  * for an array of vertices.
433  * Input:  n - number of vertices to shade
434  *         side - 0=use front material, 1=use back material
435  *         vertex - array of [n] vertex position in eye coordinates
436  *         normal - array of [n] surface normal vector
437  * Output:  indexResult - resulting array of [n] color indexes
438  */
gl_index_shade_vertices(GLcontext * ctx,GLuint side,GLuint n,GLfloat vertex[][4],GLfloat normal[][3],GLuint indexResult[])439 void gl_index_shade_vertices( GLcontext *ctx,
440                               GLuint side,
441                               GLuint n,
442                               GLfloat vertex[][4],
443                               GLfloat normal[][3],
444                               GLuint indexResult[] )
445 {
446    struct gl_material *mat = &ctx->Light.Material[side];
447    GLint j;
448 
449    /* loop over vertices */
450    for (j=0;j<n;j++) {
451       GLfloat index;
452       GLfloat diffuse, specular;  /* accumulated diffuse and specular */
453       GLfloat nx, ny, nz;  /* normal vector */
454       struct gl_light *light;
455 
456       if (side==0) {
457          /* shade frontside */
458          nx = normal[j][0];
459          ny = normal[j][1];
460          nz = normal[j][2];
461       }
462       else {
463          /* shade backside */
464          nx = -normal[j][0];
465          ny = -normal[j][1];
466          nz = -normal[j][2];
467       }
468 
469       diffuse = specular = 0.0F;
470 
471       /* Accumulate diffuse and specular from each light source */
472       for (light=ctx->Light.FirstEnabled; light; light=light->NextEnabled) {
473          GLfloat attenuation;
474          GLfloat lx, ly, lz;  /* unit vector from vertex to light */
475          GLfloat l_dot_norm;  /* dot product of l and n */
476 
477          /* compute l and attenuation */
478          if (light->Position[3]==0.0) {
479             /* directional light */
480             /* Effectively, l is a vector from the origin to the light. */
481             lx = light->VP_inf_norm[0];
482             ly = light->VP_inf_norm[1];
483             lz = light->VP_inf_norm[2];
484             attenuation = 1.0F;
485          }
486          else {
487             /* positional light */
488             GLfloat d;     /* distance from vertex to light */
489             lx = light->Position[0] - vertex[j][0];
490             ly = light->Position[1] - vertex[j][1];
491             lz = light->Position[2] - vertex[j][2];
492             d = (GLfloat) GL_SQRT( lx*lx + ly*ly + lz*lz );
493             if (d>0.001F) {
494                GLfloat invd = 1.0F / d;
495                lx *= invd;
496                ly *= invd;
497                lz *= invd;
498             }
499             attenuation = 1.0F / (light->ConstantAttenuation
500                         + d * (light->LinearAttenuation
501                         + d * light->QuadraticAttenuation));
502          }
503 
504          l_dot_norm = lx*nx + ly*ny + lz*nz;
505 
506          if (l_dot_norm>0.0F) {
507             GLfloat spot_times_atten;
508 
509             /* spotlight factor */
510             if (light->SpotCutoff==180.0F) {
511                /* not a spot light */
512                spot_times_atten = attenuation;
513             }
514             else {
515                GLfloat v[3], dot;
516                v[0] = -lx;  /* v points from light to vertex */
517                v[1] = -ly;
518                v[2] = -lz;
519                dot = DOT3( v, light->NormDirection );
520                if (dot<=0.0F || dot<light->CosCutoff) {
521                   /* outside of cone */
522                   spot_times_atten = 0.0F;
523                }
524                else {
525                   double x = dot * (EXP_TABLE_SIZE-1);
526                   int k = (int) x;
527                   GLfloat spot = light->SpotExpTable[k][0]
528                                + (x-k)*light->SpotExpTable[k][1];
529                   spot_times_atten = spot * attenuation;
530                }
531             }
532 
533             /* accumulate diffuse term */
534             diffuse += l_dot_norm * light->dli * spot_times_atten;
535 
536             /* accumulate specular term */
537             {
538                GLfloat h_x, h_y, h_z, n_dot_h, spec_coef;
539 
540                /* specular term */
541                if (ctx->Light.Model.LocalViewer) {
542                   GLfloat vx, vy, vz, vlen;
543                   vx = vertex[j][0];
544                   vy = vertex[j][1];
545                   vz = vertex[j][2];
546                   vlen = GL_SQRT( vx*vx + vy*vy + vz*vz );
547                   if (vlen>0.0001F) {
548                      GLfloat invlen = 1.0F / vlen;
549                      vx *= invlen;
550                      vy *= invlen;
551                      vz *= invlen;
552                   }
553                   h_x = lx - vx;
554                   h_y = ly - vy;
555                   h_z = lz - vz;
556                }
557                else {
558                   h_x = lx;
559                   h_y = ly;
560                   h_z = lz + 1.0F;
561                }
562                /* attention: s is not normalized, done later if necessary */
563                n_dot_h = h_x*nx + h_y*ny + h_z*nz;
564 
565                if (n_dot_h <= 0.0F) {
566                   spec_coef = 0.0F;
567                }
568                else {
569                   /* now `correct' the dot product */
570                   n_dot_h = n_dot_h / GL_SQRT(h_x*h_x + h_y*h_y + h_z*h_z);
571                   if (n_dot_h>1.0F) {
572                      spec_coef = pow( n_dot_h, mat->Shininess );
573                   }
574                   else {
575                      int k = (int) (n_dot_h * (GLfloat)(SHINE_TABLE_SIZE-1));
576                      if (mat->ShineTable[k] < 0.0F)
577                         mat->ShineTable[k] = gl_pow( n_dot_h, mat->Shininess );
578                      spec_coef = mat->ShineTable[k];
579                   }
580                }
581                specular += spec_coef * light->sli * spot_times_atten;
582             }
583          }
584 
585       } /*loop over lights*/
586 
587       /* Now compute final color index */
588       if (specular>1.0F) {
589          index = mat->SpecularIndex;
590       }
591       else {
592          GLfloat d_a, s_a;
593          d_a = mat->DiffuseIndex - mat->AmbientIndex;
594          s_a = mat->SpecularIndex - mat->AmbientIndex;
595 
596          index = mat->AmbientIndex
597                + diffuse * (1.0F-specular) * d_a
598                + specular * s_a;
599          if (index>mat->SpecularIndex) {
600             index = mat->SpecularIndex;
601          }
602       }
603       indexResult[j] = (GLuint) (GLint) index;
604 
605    } /*for vertex*/
606 }
607 
608