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 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 }; 474