mesa: Move simple_list.h to src/util.
[mesa.git] / src / mesa / tnl / t_vb_light.c
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
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 "util/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
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
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
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
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
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
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
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 */
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 */
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
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 };