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 
25 
26 
27 #include "main/glheader.h"
28 #include "main/colormac.h"
29 #include "main/light.h"
30 #include "main/macros.h"
31 #include "main/imports.h"
32 #include "main/simple_list.h"
33 #include "main/mtypes.h"
34 
35 #include "math/m_translate.h"
36 
37 #include "t_context.h"
38 #include "t_pipeline.h"
39 #include "tnl.h"
40 
41 #define LIGHT_TWOSIDE       0x1
42 #define LIGHT_MATERIAL      0x2
43 #define MAX_LIGHT_FUNC      0x4
44 
45 typedef void (*light_func)( struct gl_context *ctx,
46 			    struct vertex_buffer *VB,
47 			    struct tnl_pipeline_stage *stage,
48 			    GLvector4f *input );
49 
50 /**
51  * Information for updating current material attributes from vertex color,
52  * for GL_COLOR_MATERIAL.
53  */
54 struct material_cursor {
55    const GLfloat *ptr;    /* points to src vertex color (in VB array) */
56    GLuint stride;         /* stride to next vertex color (bytes) */
57    GLfloat *current;      /* points to material attribute to update */
58    GLuint size;           /* vertex/color size: 1, 2, 3 or 4 */
59 };
60 
61 /**
62  * Data private to this pipeline stage.
63  */
64 struct light_stage_data {
65    GLvector4f Input;
66    GLvector4f LitColor[2];
67    GLvector4f LitSecondary[2];
68    light_func *light_func_tab;
69 
70    struct material_cursor mat[MAT_ATTRIB_MAX];
71    GLuint mat_count;
72    GLuint mat_bitmask;
73 };
74 
75 
76 #define LIGHT_STAGE_DATA(stage) ((struct light_stage_data *)(stage->privatePtr))
77 
78 
79 
80 /**********************************************************************/
81 /*****                  Lighting computation                      *****/
82 /**********************************************************************/
83 
84 
85 /*
86  * Notes:
87  *   When two-sided lighting is enabled we compute the color (or index)
88  *   for both the front and back side of the primitive.  Then, when the
89  *   orientation of the facet is later learned, we can determine which
90  *   color (or index) to use for rendering.
91  *
92  *   KW: We now know orientation in advance and only shade for
93  *       the side or sides which are actually required.
94  *
95  * Variables:
96  *   n = normal vector
97  *   V = vertex position
98  *   P = light source position
99  *   Pe = (0,0,0,1)
100  *
101  * Precomputed:
102  *   IF P[3]==0 THEN
103  *       // light at infinity
104  *       IF local_viewer THEN
105  *           _VP_inf_norm = unit vector from V to P      // Precompute
106  *       ELSE
107  *           // eye at infinity
108  *           _h_inf_norm = Normalize( VP + <0,0,1> )     // Precompute
109  *       ENDIF
110  *   ENDIF
111  *
112  * Functions:
113  *   Normalize( v ) = normalized vector v
114  *   Magnitude( v ) = length of vector v
115  */
116 
117 
118 
119 static void
validate_shine_table(struct gl_context * ctx,GLuint side,GLfloat shininess)120 validate_shine_table( struct gl_context *ctx, GLuint side, GLfloat shininess )
121 {
122    TNLcontext *tnl = TNL_CONTEXT(ctx);
123    struct tnl_shine_tab *list = tnl->_ShineTabList;
124    struct tnl_shine_tab *s;
125 
126    ASSERT(side < 2);
127 
128    foreach(s, list)
129       if ( s->shininess == shininess )
130 	 break;
131 
132    if (s == list) {
133       GLint j;
134       GLfloat *m;
135 
136       foreach(s, list)
137 	 if (s->refcount == 0)
138 	    break;
139 
140       m = s->tab;
141       m[0] = 0.0;
142       if (shininess == 0.0) {
143 	 for (j = 1 ; j <= SHINE_TABLE_SIZE ; j++)
144 	    m[j] = 1.0;
145       }
146       else {
147 	 for (j = 1 ; j < SHINE_TABLE_SIZE ; j++) {
148             GLdouble t, x = j / (GLfloat) (SHINE_TABLE_SIZE - 1);
149             if (x < 0.005) /* underflow check */
150                x = 0.005;
151             t = pow(x, shininess);
152 	    if (t > 1e-20)
153 	       m[j] = (GLfloat) t;
154 	    else
155 	       m[j] = 0.0;
156 	 }
157 	 m[SHINE_TABLE_SIZE] = 1.0;
158       }
159 
160       s->shininess = shininess;
161    }
162 
163    if (tnl->_ShineTable[side])
164       tnl->_ShineTable[side]->refcount--;
165 
166    tnl->_ShineTable[side] = s;
167    move_to_tail( list, s );
168    s->refcount++;
169 }
170 
171 
172 void
_tnl_validate_shine_tables(struct gl_context * ctx)173 _tnl_validate_shine_tables( struct gl_context *ctx )
174 {
175    TNLcontext *tnl = TNL_CONTEXT(ctx);
176    GLfloat shininess;
177 
178    shininess = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SHININESS][0];
179    if (!tnl->_ShineTable[0] || tnl->_ShineTable[0]->shininess != shininess)
180       validate_shine_table( ctx, 0, shininess );
181 
182    shininess = ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_SHININESS][0];
183    if (!tnl->_ShineTable[1] || tnl->_ShineTable[1]->shininess != shininess)
184       validate_shine_table( ctx, 1, shininess );
185 }
186 
187 
188 /**
189  * In the case of colormaterial, the effected material attributes
190  * should already have been bound to point to the incoming color data,
191  * prior to running the pipeline.
192  * This function copies the vertex's color to the material attributes
193  * which are tracking glColor.
194  * It's called per-vertex in the lighting loop.
195  */
196 static void
update_materials(struct gl_context * ctx,struct light_stage_data * store)197 update_materials(struct gl_context *ctx, struct light_stage_data *store)
198 {
199    GLuint i;
200 
201    for (i = 0 ; i < store->mat_count ; i++) {
202       /* update the material */
203       COPY_CLEAN_4V(store->mat[i].current, store->mat[i].size, store->mat[i].ptr);
204       /* increment src vertex color pointer */
205       STRIDE_F(store->mat[i].ptr, store->mat[i].stride);
206    }
207 
208    /* recompute derived light/material values */
209    _mesa_update_material( ctx, store->mat_bitmask );
210    /* XXX we should only call this if we're tracking/changing the specular
211     * exponent.
212     */
213    _tnl_validate_shine_tables( ctx );
214 }
215 
216 
217 /**
218  * Prepare things prior to running the lighting stage.
219  * Return number of material attributes which will track vertex color.
220  */
221 static GLuint
prepare_materials(struct gl_context * ctx,struct vertex_buffer * VB,struct light_stage_data * store)222 prepare_materials(struct gl_context *ctx,
223                   struct vertex_buffer *VB, struct light_stage_data *store)
224 {
225    GLuint i;
226 
227    store->mat_count = 0;
228    store->mat_bitmask = 0;
229 
230    /* Examine the _ColorMaterialBitmask to determine which materials
231     * track vertex color.  Override the material attribute's pointer
232     * with the color pointer for each one.
233     */
234    if (ctx->Light.ColorMaterialEnabled) {
235       const GLuint bitmask = ctx->Light._ColorMaterialBitmask;
236       for (i = 0 ; i < MAT_ATTRIB_MAX ; i++)
237 	 if (bitmask & (1<<i))
238 	    VB->AttribPtr[_TNL_ATTRIB_MAT_FRONT_AMBIENT + i] = VB->AttribPtr[_TNL_ATTRIB_COLOR0];
239    }
240 
241    /* Now, for each material attribute that's tracking vertex color, save
242     * some values (ptr, stride, size, current) that we'll need in
243     * update_materials(), above, that'll actually copy the vertex color to
244     * the material attribute(s).
245     */
246    for (i = _TNL_FIRST_MAT; i <= _TNL_LAST_MAT; i++) {
247       if (VB->AttribPtr[i]->stride) {
248 	 const GLuint j = store->mat_count++;
249 	 const GLuint attr = i - _TNL_ATTRIB_MAT_FRONT_AMBIENT;
250 	 store->mat[j].ptr    = VB->AttribPtr[i]->start;
251 	 store->mat[j].stride = VB->AttribPtr[i]->stride;
252 	 store->mat[j].size   = VB->AttribPtr[i]->size;
253 	 store->mat[j].current = ctx->Light.Material.Attrib[attr];
254 	 store->mat_bitmask |= (1<<attr);
255       }
256    }
257 
258    /* FIXME: Is this already done?
259     */
260    _mesa_update_material( ctx, ~0 );
261 
262    _tnl_validate_shine_tables( ctx );
263 
264    return store->mat_count;
265 }
266 
267 /*
268  * Compute dp ^ SpecularExponent.
269  * Lerp between adjacent values in the f(x) lookup table, giving a
270  * continuous function, with adequate overall accuracy.  (Though still
271  * pretty good compared to a straight lookup).
272  */
273 static inline GLfloat
lookup_shininess(const struct gl_context * ctx,GLuint face,GLfloat dp)274 lookup_shininess(const struct gl_context *ctx, GLuint face, GLfloat dp)
275 {
276    TNLcontext *tnl = TNL_CONTEXT(ctx);
277    const struct tnl_shine_tab *tab = tnl->_ShineTable[face];
278    float f = dp * (SHINE_TABLE_SIZE - 1);
279    int k = (int) f;
280    if (k < 0 /* gcc may cast an overflow float value to negative int value */
281 	|| k > SHINE_TABLE_SIZE - 2)
282       return powf(dp, tab->shininess);
283    else
284       return tab->tab[k] + (f - k) * (tab->tab[k+1] - tab->tab[k]);
285 }
286 
287 /* Tables for all the shading functions.
288  */
289 static light_func _tnl_light_tab[MAX_LIGHT_FUNC];
290 static light_func _tnl_light_fast_tab[MAX_LIGHT_FUNC];
291 static light_func _tnl_light_fast_single_tab[MAX_LIGHT_FUNC];
292 static light_func _tnl_light_spec_tab[MAX_LIGHT_FUNC];
293 
294 #define TAG(x)           x
295 #define IDX              (0)
296 #include "t_vb_lighttmp.h"
297 
298 #define TAG(x)           x##_twoside
299 #define IDX              (LIGHT_TWOSIDE)
300 #include "t_vb_lighttmp.h"
301 
302 #define TAG(x)           x##_material
303 #define IDX              (LIGHT_MATERIAL)
304 #include "t_vb_lighttmp.h"
305 
306 #define TAG(x)           x##_twoside_material
307 #define IDX              (LIGHT_TWOSIDE|LIGHT_MATERIAL)
308 #include "t_vb_lighttmp.h"
309 
310 
init_lighting_tables(void)311 static void init_lighting_tables( void )
312 {
313    static int done;
314 
315    if (!done) {
316       init_light_tab();
317       init_light_tab_twoside();
318       init_light_tab_material();
319       init_light_tab_twoside_material();
320       done = 1;
321    }
322 }
323 
324 
run_lighting(struct gl_context * ctx,struct tnl_pipeline_stage * stage)325 static GLboolean run_lighting( struct gl_context *ctx,
326 			       struct tnl_pipeline_stage *stage )
327 {
328    struct light_stage_data *store = LIGHT_STAGE_DATA(stage);
329    TNLcontext *tnl = TNL_CONTEXT(ctx);
330    struct vertex_buffer *VB = &tnl->vb;
331    GLvector4f *input = ctx->_NeedEyeCoords ? VB->EyePtr : VB->AttribPtr[_TNL_ATTRIB_POS];
332    GLuint idx;
333 
334    if (!ctx->Light.Enabled || ctx->VertexProgram._Current)
335       return GL_TRUE;
336 
337    /* Make sure we can talk about position x,y and z:
338     */
339    if (input->size <= 2 && input == VB->AttribPtr[_TNL_ATTRIB_POS]) {
340 
341       _math_trans_4f( store->Input.data,
342 		      VB->AttribPtr[_TNL_ATTRIB_POS]->data,
343 		      VB->AttribPtr[_TNL_ATTRIB_POS]->stride,
344 		      GL_FLOAT,
345 		      VB->AttribPtr[_TNL_ATTRIB_POS]->size,
346 		      0,
347 		      VB->Count );
348 
349       if (input->size <= 2) {
350 	 /* Clean z.
351 	  */
352 	 _mesa_vector4f_clean_elem(&store->Input, VB->Count, 2);
353       }
354 
355       if (input->size <= 1) {
356 	 /* Clean y.
357 	  */
358 	 _mesa_vector4f_clean_elem(&store->Input, VB->Count, 1);
359       }
360 
361       input = &store->Input;
362    }
363 
364    idx = 0;
365 
366    if (prepare_materials( ctx, VB, store ))
367       idx |= LIGHT_MATERIAL;
368 
369    if (ctx->Light.Model.TwoSide)
370       idx |= LIGHT_TWOSIDE;
371 
372    /* The individual functions know about replaying side-effects
373     * vs. full re-execution.
374     */
375    store->light_func_tab[idx]( ctx, VB, stage, input );
376 
377    return GL_TRUE;
378 }
379 
380 
381 /* Called in place of do_lighting when the light table may have changed.
382  */
validate_lighting(struct gl_context * ctx,struct tnl_pipeline_stage * stage)383 static void validate_lighting( struct gl_context *ctx,
384 					struct tnl_pipeline_stage *stage )
385 {
386    light_func *tab;
387 
388    if (!ctx->Light.Enabled || ctx->VertexProgram._Current)
389       return;
390 
391    if (ctx->Light._NeedVertices) {
392       if (ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR)
393 	 tab = _tnl_light_spec_tab;
394       else
395 	 tab = _tnl_light_tab;
396    }
397    else {
398       if (ctx->Light.EnabledList.next == ctx->Light.EnabledList.prev)
399 	 tab = _tnl_light_fast_single_tab;
400       else
401 	 tab = _tnl_light_fast_tab;
402    }
403 
404 
405    LIGHT_STAGE_DATA(stage)->light_func_tab = tab;
406 
407    /* This and the above should only be done on _NEW_LIGHT:
408     */
409    TNL_CONTEXT(ctx)->Driver.NotifyMaterialChange( ctx );
410 }
411 
412 
413 
414 /* Called the first time stage->run is called.  In effect, don't
415  * allocate data until the first time the stage is run.
416  */
init_lighting(struct gl_context * ctx,struct tnl_pipeline_stage * stage)417 static GLboolean init_lighting( struct gl_context *ctx,
418 				struct tnl_pipeline_stage *stage )
419 {
420    TNLcontext *tnl = TNL_CONTEXT(ctx);
421    struct light_stage_data *store;
422    GLuint size = tnl->vb.Size;
423 
424    stage->privatePtr = MALLOC(sizeof(*store));
425    store = LIGHT_STAGE_DATA(stage);
426    if (!store)
427       return GL_FALSE;
428 
429    /* Do onetime init.
430     */
431    init_lighting_tables();
432 
433    _mesa_vector4f_alloc( &store->Input, 0, size, 32 );
434    _mesa_vector4f_alloc( &store->LitColor[0], 0, size, 32 );
435    _mesa_vector4f_alloc( &store->LitColor[1], 0, size, 32 );
436    _mesa_vector4f_alloc( &store->LitSecondary[0], 0, size, 32 );
437    _mesa_vector4f_alloc( &store->LitSecondary[1], 0, size, 32 );
438 
439    store->LitColor[0].size = 4;
440    store->LitColor[1].size = 4;
441    store->LitSecondary[0].size = 3;
442    store->LitSecondary[1].size = 3;
443 
444    return GL_TRUE;
445 }
446 
447 
448 
449 
dtr(struct tnl_pipeline_stage * stage)450 static void dtr( struct tnl_pipeline_stage *stage )
451 {
452    struct light_stage_data *store = LIGHT_STAGE_DATA(stage);
453 
454    if (store) {
455       _mesa_vector4f_free( &store->Input );
456       _mesa_vector4f_free( &store->LitColor[0] );
457       _mesa_vector4f_free( &store->LitColor[1] );
458       _mesa_vector4f_free( &store->LitSecondary[0] );
459       _mesa_vector4f_free( &store->LitSecondary[1] );
460       FREE( store );
461       stage->privatePtr = NULL;
462    }
463 }
464 
465 const struct tnl_pipeline_stage _tnl_lighting_stage =
466 {
467    "lighting",			/* name */
468    NULL,			/* private_data */
469    init_lighting,
470    dtr,				/* destroy */
471    validate_lighting,
472    run_lighting
473 };
474