d286129650bc4cfb5ff6ff08f3011c711b28e74b
[mesa.git] / src / mesa / tnl / t_vb_light.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.3
4 *
5 * Copyright (C) 1999-2005 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 "glheader.h"
28 #include "colormac.h"
29 #include "light.h"
30 #include "macros.h"
31 #include "imports.h"
32 #include "simple_list.h"
33 #include "mtypes.h"
34
35 #include "math/m_translate.h"
36
37 #include "t_context.h"
38 #include "t_pipeline.h"
39
40 #define LIGHT_TWOSIDE 0x1
41 #define LIGHT_MATERIAL 0x2
42 #define MAX_LIGHT_FUNC 0x4
43
44 typedef void (*light_func)( GLcontext *ctx,
45 struct vertex_buffer *VB,
46 struct tnl_pipeline_stage *stage,
47 GLvector4f *input );
48
49 struct material_cursor {
50 const GLfloat *ptr;
51 GLuint stride;
52 GLfloat *current;
53 GLuint size; /* 1, 2, 3 or 4 */
54 };
55
56 struct light_stage_data {
57 GLvector4f Input;
58 GLvector4f LitColor[2];
59 GLvector4f LitSecondary[2];
60 GLvector4f LitIndex[2];
61 light_func *light_func_tab;
62
63 struct material_cursor mat[MAT_ATTRIB_MAX];
64 GLuint mat_count;
65 GLuint mat_bitmask;
66 };
67
68
69 #define LIGHT_STAGE_DATA(stage) ((struct light_stage_data *)(stage->privatePtr))
70
71
72
73 /* In the case of colormaterial, the effected material attributes
74 * should already have been bound to point to the incoming color data,
75 * prior to running the pipeline.
76 */
77 static void update_materials( GLcontext *ctx,
78 struct light_stage_data *store )
79 {
80 GLuint i;
81
82 for (i = 0 ; i < store->mat_count ; i++) {
83 COPY_CLEAN_4V(store->mat[i].current, store->mat[i].size, store->mat[i].ptr);
84 STRIDE_F(store->mat[i].ptr, store->mat[i].stride);
85 }
86
87 _mesa_update_material( ctx, store->mat_bitmask );
88 _mesa_validate_all_lighting_tables( ctx );
89 }
90
91 static GLuint prepare_materials( GLcontext *ctx,
92 struct vertex_buffer *VB,
93 struct light_stage_data *store )
94 {
95 GLuint i;
96
97 store->mat_count = 0;
98 store->mat_bitmask = 0;
99
100 /* If ColorMaterial enabled, overwrite affected AttrPtr's with
101 * the color pointer. This could be done earlier.
102 */
103 if (ctx->Light.ColorMaterialEnabled) {
104 GLuint bitmask = ctx->Light.ColorMaterialBitmask;
105 for (i = 0 ; i < MAT_ATTRIB_MAX ; i++)
106 if (bitmask & (1<<i))
107 VB->AttribPtr[_TNL_ATTRIB_MAT_FRONT_AMBIENT + i] = VB->ColorPtr[0];
108 }
109
110 for (i = _TNL_ATTRIB_MAT_FRONT_AMBIENT ; i < _TNL_ATTRIB_INDEX ; i++) {
111 if (VB->AttribPtr[i]->stride) {
112 GLuint j = store->mat_count++;
113 GLuint attr = i - _TNL_ATTRIB_MAT_FRONT_AMBIENT;
114 store->mat[j].ptr = VB->AttribPtr[i]->start;
115 store->mat[j].stride = VB->AttribPtr[i]->stride;
116 store->mat[j].size = VB->AttribPtr[i]->size;
117 store->mat[j].current = ctx->Light.Material.Attrib[attr];
118 store->mat_bitmask |= (1<<attr);
119 }
120 }
121
122
123 /* FIXME: Is this already done?
124 */
125 _mesa_update_material( ctx, ~0 );
126 _mesa_validate_all_lighting_tables( ctx );
127
128 return store->mat_count;
129 }
130
131 /* Tables for all the shading functions.
132 */
133 static light_func _tnl_light_tab[MAX_LIGHT_FUNC];
134 static light_func _tnl_light_fast_tab[MAX_LIGHT_FUNC];
135 static light_func _tnl_light_fast_single_tab[MAX_LIGHT_FUNC];
136 static light_func _tnl_light_spec_tab[MAX_LIGHT_FUNC];
137 static light_func _tnl_light_ci_tab[MAX_LIGHT_FUNC];
138
139 #define TAG(x) x
140 #define IDX (0)
141 #include "t_vb_lighttmp.h"
142
143 #define TAG(x) x##_twoside
144 #define IDX (LIGHT_TWOSIDE)
145 #include "t_vb_lighttmp.h"
146
147 #define TAG(x) x##_material
148 #define IDX (LIGHT_MATERIAL)
149 #include "t_vb_lighttmp.h"
150
151 #define TAG(x) x##_twoside_material
152 #define IDX (LIGHT_TWOSIDE|LIGHT_MATERIAL)
153 #include "t_vb_lighttmp.h"
154
155
156 static void init_lighting( void )
157 {
158 static int done;
159
160 if (!done) {
161 init_light_tab();
162 init_light_tab_twoside();
163 init_light_tab_material();
164 init_light_tab_twoside_material();
165 done = 1;
166 }
167 }
168
169
170 static GLboolean run_lighting( GLcontext *ctx,
171 struct tnl_pipeline_stage *stage )
172 {
173 struct light_stage_data *store = LIGHT_STAGE_DATA(stage);
174 TNLcontext *tnl = TNL_CONTEXT(ctx);
175 struct vertex_buffer *VB = &tnl->vb;
176 GLvector4f *input = ctx->_NeedEyeCoords ? VB->EyePtr : VB->ObjPtr;
177 GLuint idx;
178
179 /* Make sure we can talk about position x,y and z:
180 */
181 if (stage->changed_inputs & _TNL_BIT_POS) {
182 if (input->size <= 2 && input == VB->ObjPtr) {
183
184 _math_trans_4f( store->Input.data,
185 VB->ObjPtr->data,
186 VB->ObjPtr->stride,
187 GL_FLOAT,
188 VB->ObjPtr->size,
189 0,
190 VB->Count );
191
192 if (input->size <= 2) {
193 /* Clean z.
194 */
195 _mesa_vector4f_clean_elem(&store->Input, VB->Count, 2);
196 }
197
198 if (input->size <= 1) {
199 /* Clean y.
200 */
201 _mesa_vector4f_clean_elem(&store->Input, VB->Count, 1);
202 }
203
204 input = &store->Input;
205 }
206 }
207
208 idx = 0;
209
210 if (prepare_materials( ctx, VB, store ))
211 idx |= LIGHT_MATERIAL;
212
213 if (ctx->Light.Model.TwoSide)
214 idx |= LIGHT_TWOSIDE;
215
216 /* The individual functions know about replaying side-effects
217 * vs. full re-execution.
218 */
219 store->light_func_tab[idx]( ctx, VB, stage, input );
220
221 VB->AttribPtr[_TNL_ATTRIB_COLOR0] = VB->ColorPtr[0];
222 VB->AttribPtr[_TNL_ATTRIB_COLOR1] = VB->SecondaryColorPtr[0];
223 VB->AttribPtr[_TNL_ATTRIB_INDEX] = VB->IndexPtr[0];
224
225 return GL_TRUE;
226 }
227
228
229 /* Called in place of do_lighting when the light table may have changed.
230 */
231 static GLboolean run_validate_lighting( GLcontext *ctx,
232 struct tnl_pipeline_stage *stage )
233 {
234 light_func *tab;
235
236 if (ctx->Visual.rgbMode) {
237 if (ctx->Light._NeedVertices) {
238 if (ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR)
239 tab = _tnl_light_spec_tab;
240 else
241 tab = _tnl_light_tab;
242 }
243 else {
244 if (ctx->Light.EnabledList.next == ctx->Light.EnabledList.prev)
245 tab = _tnl_light_fast_single_tab;
246 else
247 tab = _tnl_light_fast_tab;
248 }
249 }
250 else
251 tab = _tnl_light_ci_tab;
252
253
254 LIGHT_STAGE_DATA(stage)->light_func_tab = tab;
255
256 /* This and the above should only be done on _NEW_LIGHT:
257 */
258 TNL_CONTEXT(ctx)->Driver.NotifyMaterialChange( ctx );
259
260 /* Now run the stage...
261 */
262 stage->run = run_lighting;
263 return stage->run( ctx, stage );
264 }
265
266
267
268 /* Called the first time stage->run is called. In effect, don't
269 * allocate data until the first time the stage is run.
270 */
271 static GLboolean run_init_lighting( GLcontext *ctx,
272 struct tnl_pipeline_stage *stage )
273 {
274 TNLcontext *tnl = TNL_CONTEXT(ctx);
275 struct light_stage_data *store;
276 GLuint size = tnl->vb.Size;
277
278 stage->privatePtr = MALLOC(sizeof(*store));
279 store = LIGHT_STAGE_DATA(stage);
280 if (!store)
281 return GL_FALSE;
282
283 /* Do onetime init.
284 */
285 init_lighting();
286
287 _mesa_vector4f_alloc( &store->Input, 0, size, 32 );
288 _mesa_vector4f_alloc( &store->LitColor[0], 0, size, 32 );
289 _mesa_vector4f_alloc( &store->LitColor[1], 0, size, 32 );
290 _mesa_vector4f_alloc( &store->LitSecondary[0], 0, size, 32 );
291 _mesa_vector4f_alloc( &store->LitSecondary[1], 0, size, 32 );
292 _mesa_vector4f_alloc( &store->LitIndex[0], 0, size, 32 );
293 _mesa_vector4f_alloc( &store->LitIndex[1], 0, size, 32 );
294
295 store->LitColor[0].size = 4;
296 store->LitColor[1].size = 4;
297 store->LitSecondary[0].size = 3;
298 store->LitSecondary[1].size = 3;
299
300 store->LitIndex[0].size = 1;
301 store->LitIndex[0].stride = sizeof(GLfloat);
302 store->LitIndex[1].size = 1;
303 store->LitIndex[1].stride = sizeof(GLfloat);
304
305 /* Now validate the stage derived data...
306 */
307 stage->run = run_validate_lighting;
308 return stage->run( ctx, stage );
309 }
310
311
312
313 /*
314 * Check if lighting is enabled. If so, configure the pipeline stage's
315 * type, inputs, and outputs.
316 */
317 static void check_lighting( GLcontext *ctx, struct tnl_pipeline_stage *stage )
318 {
319 stage->active = ctx->Light.Enabled && !ctx->VertexProgram._Enabled;
320 if (stage->active) {
321 if (stage->privatePtr)
322 stage->run = run_validate_lighting;
323 stage->inputs = _TNL_BIT_NORMAL|_TNL_BITS_MAT_ANY;
324 if (ctx->Light._NeedVertices)
325 stage->inputs |= _TNL_BIT_POS;
326 if (ctx->Light.ColorMaterialEnabled)
327 stage->inputs |= _TNL_BIT_COLOR0;
328
329 stage->outputs = _TNL_BIT_COLOR0;
330 if (ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR)
331 stage->outputs |= _TNL_BIT_COLOR1;
332 }
333 }
334
335
336 static void dtr( struct tnl_pipeline_stage *stage )
337 {
338 struct light_stage_data *store = LIGHT_STAGE_DATA(stage);
339
340 if (store) {
341 _mesa_vector4f_free( &store->Input );
342 _mesa_vector4f_free( &store->LitColor[0] );
343 _mesa_vector4f_free( &store->LitColor[1] );
344 _mesa_vector4f_free( &store->LitSecondary[0] );
345 _mesa_vector4f_free( &store->LitSecondary[1] );
346 _mesa_vector4f_free( &store->LitIndex[0] );
347 _mesa_vector4f_free( &store->LitIndex[1] );
348 FREE( store );
349 stage->privatePtr = NULL;
350 }
351 }
352
353 const struct tnl_pipeline_stage _tnl_lighting_stage =
354 {
355 "lighting", /* name */
356 _NEW_LIGHT|_NEW_PROGRAM, /* recheck */
357 _NEW_LIGHT|_NEW_MODELVIEW, /* recalc -- modelview dependency
358 * otherwise not captured by inputs
359 * (which may be _TNL_BIT_POS) */
360 GL_FALSE, /* active? */
361 0, /* inputs */
362 0, /* outputs */
363 0, /* changed_inputs */
364 NULL, /* private_data */
365 dtr, /* destroy */
366 check_lighting, /* check */
367 run_init_lighting /* run -- initially set to ctr */
368 };