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