| /* |
| * Mesa 3-D graphics library |
| * Version: 7.1 |
| * |
| * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
| * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /** |
| * \file prog_statevars.c |
| * Program state variable management. |
| * \author Brian Paul |
| */ |
| |
| |
| #include "main/glheader.h" |
| #include "main/context.h" |
| #include "main/imports.h" |
| #include "main/macros.h" |
| #include "main/mtypes.h" |
| #include "prog_statevars.h" |
| #include "prog_parameter.h" |
| |
| |
| /** |
| * Use the list of tokens in the state[] array to find global GL state |
| * and return it in <value>. Usually, four values are returned in <value> |
| * but matrix queries may return as many as 16 values. |
| * This function is used for ARB vertex/fragment programs. |
| * The program parser will produce the state[] values. |
| */ |
| static void |
| _mesa_fetch_state(struct gl_context *ctx, const gl_state_index state[], |
| GLfloat *value) |
| { |
| switch (state[0]) { |
| case STATE_MATERIAL: |
| { |
| /* state[1] is either 0=front or 1=back side */ |
| const GLuint face = (GLuint) state[1]; |
| const struct gl_material *mat = &ctx->Light.Material; |
| ASSERT(face == 0 || face == 1); |
| /* we rely on tokens numbered so that _BACK_ == _FRONT_+ 1 */ |
| ASSERT(MAT_ATTRIB_FRONT_AMBIENT + 1 == MAT_ATTRIB_BACK_AMBIENT); |
| /* XXX we could get rid of this switch entirely with a little |
| * work in arbprogparse.c's parse_state_single_item(). |
| */ |
| /* state[2] is the material attribute */ |
| switch (state[2]) { |
| case STATE_AMBIENT: |
| COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_AMBIENT + face]); |
| return; |
| case STATE_DIFFUSE: |
| COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_DIFFUSE + face]); |
| return; |
| case STATE_SPECULAR: |
| COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_SPECULAR + face]); |
| return; |
| case STATE_EMISSION: |
| COPY_4V(value, mat->Attrib[MAT_ATTRIB_FRONT_EMISSION + face]); |
| return; |
| case STATE_SHININESS: |
| value[0] = mat->Attrib[MAT_ATTRIB_FRONT_SHININESS + face][0]; |
| value[1] = 0.0F; |
| value[2] = 0.0F; |
| value[3] = 1.0F; |
| return; |
| default: |
| _mesa_problem(ctx, "Invalid material state in fetch_state"); |
| return; |
| } |
| } |
| case STATE_LIGHT: |
| { |
| /* state[1] is the light number */ |
| const GLuint ln = (GLuint) state[1]; |
| /* state[2] is the light attribute */ |
| switch (state[2]) { |
| case STATE_AMBIENT: |
| COPY_4V(value, ctx->Light.Light[ln].Ambient); |
| return; |
| case STATE_DIFFUSE: |
| COPY_4V(value, ctx->Light.Light[ln].Diffuse); |
| return; |
| case STATE_SPECULAR: |
| COPY_4V(value, ctx->Light.Light[ln].Specular); |
| return; |
| case STATE_POSITION: |
| COPY_4V(value, ctx->Light.Light[ln].EyePosition); |
| return; |
| case STATE_ATTENUATION: |
| value[0] = ctx->Light.Light[ln].ConstantAttenuation; |
| value[1] = ctx->Light.Light[ln].LinearAttenuation; |
| value[2] = ctx->Light.Light[ln].QuadraticAttenuation; |
| value[3] = ctx->Light.Light[ln].SpotExponent; |
| return; |
| case STATE_SPOT_DIRECTION: |
| COPY_3V(value, ctx->Light.Light[ln].SpotDirection); |
| value[3] = ctx->Light.Light[ln]._CosCutoff; |
| return; |
| case STATE_SPOT_CUTOFF: |
| value[0] = ctx->Light.Light[ln].SpotCutoff; |
| return; |
| case STATE_HALF_VECTOR: |
| { |
| static const GLfloat eye_z[] = {0, 0, 1}; |
| GLfloat p[3]; |
| /* Compute infinite half angle vector: |
| * halfVector = normalize(normalize(lightPos) + (0, 0, 1)) |
| * light.EyePosition.w should be 0 for infinite lights. |
| */ |
| COPY_3V(p, ctx->Light.Light[ln].EyePosition); |
| NORMALIZE_3FV(p); |
| ADD_3V(value, p, eye_z); |
| NORMALIZE_3FV(value); |
| value[3] = 1.0; |
| } |
| return; |
| default: |
| _mesa_problem(ctx, "Invalid light state in fetch_state"); |
| return; |
| } |
| } |
| case STATE_LIGHTMODEL_AMBIENT: |
| COPY_4V(value, ctx->Light.Model.Ambient); |
| return; |
| case STATE_LIGHTMODEL_SCENECOLOR: |
| if (state[1] == 0) { |
| /* front */ |
| GLint i; |
| for (i = 0; i < 3; i++) { |
| value[i] = ctx->Light.Model.Ambient[i] |
| * ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT][i] |
| + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_EMISSION][i]; |
| } |
| value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE][3]; |
| } |
| else { |
| /* back */ |
| GLint i; |
| for (i = 0; i < 3; i++) { |
| value[i] = ctx->Light.Model.Ambient[i] |
| * ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_AMBIENT][i] |
| + ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_EMISSION][i]; |
| } |
| value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_DIFFUSE][3]; |
| } |
| return; |
| case STATE_LIGHTPROD: |
| { |
| const GLuint ln = (GLuint) state[1]; |
| const GLuint face = (GLuint) state[2]; |
| GLint i; |
| ASSERT(face == 0 || face == 1); |
| switch (state[3]) { |
| case STATE_AMBIENT: |
| for (i = 0; i < 3; i++) { |
| value[i] = ctx->Light.Light[ln].Ambient[i] * |
| ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][i]; |
| } |
| /* [3] = material alpha */ |
| value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][3]; |
| return; |
| case STATE_DIFFUSE: |
| for (i = 0; i < 3; i++) { |
| value[i] = ctx->Light.Light[ln].Diffuse[i] * |
| ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][i]; |
| } |
| /* [3] = material alpha */ |
| value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][3]; |
| return; |
| case STATE_SPECULAR: |
| for (i = 0; i < 3; i++) { |
| value[i] = ctx->Light.Light[ln].Specular[i] * |
| ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][i]; |
| } |
| /* [3] = material alpha */ |
| value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][3]; |
| return; |
| default: |
| _mesa_problem(ctx, "Invalid lightprod state in fetch_state"); |
| return; |
| } |
| } |
| case STATE_TEXGEN: |
| { |
| /* state[1] is the texture unit */ |
| const GLuint unit = (GLuint) state[1]; |
| /* state[2] is the texgen attribute */ |
| switch (state[2]) { |
| case STATE_TEXGEN_EYE_S: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenS.EyePlane); |
| return; |
| case STATE_TEXGEN_EYE_T: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenT.EyePlane); |
| return; |
| case STATE_TEXGEN_EYE_R: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenR.EyePlane); |
| return; |
| case STATE_TEXGEN_EYE_Q: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenQ.EyePlane); |
| return; |
| case STATE_TEXGEN_OBJECT_S: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenS.ObjectPlane); |
| return; |
| case STATE_TEXGEN_OBJECT_T: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenT.ObjectPlane); |
| return; |
| case STATE_TEXGEN_OBJECT_R: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenR.ObjectPlane); |
| return; |
| case STATE_TEXGEN_OBJECT_Q: |
| COPY_4V(value, ctx->Texture.Unit[unit].GenQ.ObjectPlane); |
| return; |
| default: |
| _mesa_problem(ctx, "Invalid texgen state in fetch_state"); |
| return; |
| } |
| } |
| case STATE_TEXENV_COLOR: |
| { |
| /* state[1] is the texture unit */ |
| const GLuint unit = (GLuint) state[1]; |
| if(ctx->Color._ClampFragmentColor) |
| COPY_4V(value, ctx->Texture.Unit[unit].EnvColor); |
| else |
| COPY_4V(value, ctx->Texture.Unit[unit].EnvColorUnclamped); |
| } |
| return; |
| case STATE_FOG_COLOR: |
| if(ctx->Color._ClampFragmentColor) |
| COPY_4V(value, ctx->Fog.Color); |
| else |
| COPY_4V(value, ctx->Fog.ColorUnclamped); |
| return; |
| case STATE_FOG_PARAMS: |
| value[0] = ctx->Fog.Density; |
| value[1] = ctx->Fog.Start; |
| value[2] = ctx->Fog.End; |
| value[3] = (ctx->Fog.End == ctx->Fog.Start) |
| ? 1.0f : (GLfloat)(1.0 / (ctx->Fog.End - ctx->Fog.Start)); |
| return; |
| case STATE_CLIPPLANE: |
| { |
| const GLuint plane = (GLuint) state[1]; |
| COPY_4V(value, ctx->Transform.EyeUserPlane[plane]); |
| } |
| return; |
| case STATE_POINT_SIZE: |
| value[0] = ctx->Point.Size; |
| value[1] = ctx->Point.MinSize; |
| value[2] = ctx->Point.MaxSize; |
| value[3] = ctx->Point.Threshold; |
| return; |
| case STATE_POINT_ATTENUATION: |
| value[0] = ctx->Point.Params[0]; |
| value[1] = ctx->Point.Params[1]; |
| value[2] = ctx->Point.Params[2]; |
| value[3] = 1.0F; |
| return; |
| case STATE_MODELVIEW_MATRIX: |
| case STATE_PROJECTION_MATRIX: |
| case STATE_MVP_MATRIX: |
| case STATE_TEXTURE_MATRIX: |
| case STATE_PROGRAM_MATRIX: |
| { |
| /* state[0] = modelview, projection, texture, etc. */ |
| /* state[1] = which texture matrix or program matrix */ |
| /* state[2] = first row to fetch */ |
| /* state[3] = last row to fetch */ |
| /* state[4] = transpose, inverse or invtrans */ |
| const GLmatrix *matrix; |
| const gl_state_index mat = state[0]; |
| const GLuint index = (GLuint) state[1]; |
| const GLuint firstRow = (GLuint) state[2]; |
| const GLuint lastRow = (GLuint) state[3]; |
| const gl_state_index modifier = state[4]; |
| const GLfloat *m; |
| GLuint row, i; |
| ASSERT(firstRow >= 0); |
| ASSERT(firstRow < 4); |
| ASSERT(lastRow >= 0); |
| ASSERT(lastRow < 4); |
| if (mat == STATE_MODELVIEW_MATRIX) { |
| matrix = ctx->ModelviewMatrixStack.Top; |
| } |
| else if (mat == STATE_PROJECTION_MATRIX) { |
| matrix = ctx->ProjectionMatrixStack.Top; |
| } |
| else if (mat == STATE_MVP_MATRIX) { |
| matrix = &ctx->_ModelProjectMatrix; |
| } |
| else if (mat == STATE_TEXTURE_MATRIX) { |
| ASSERT(index < Elements(ctx->TextureMatrixStack)); |
| matrix = ctx->TextureMatrixStack[index].Top; |
| } |
| else if (mat == STATE_PROGRAM_MATRIX) { |
| ASSERT(index < Elements(ctx->ProgramMatrixStack)); |
| matrix = ctx->ProgramMatrixStack[index].Top; |
| } |
| else { |
| _mesa_problem(ctx, "Bad matrix name in _mesa_fetch_state()"); |
| return; |
| } |
| if (modifier == STATE_MATRIX_INVERSE || |
| modifier == STATE_MATRIX_INVTRANS) { |
| /* Be sure inverse is up to date: |
| */ |
| _math_matrix_alloc_inv( (GLmatrix *) matrix ); |
| _math_matrix_analyse( (GLmatrix*) matrix ); |
| m = matrix->inv; |
| } |
| else { |
| m = matrix->m; |
| } |
| if (modifier == STATE_MATRIX_TRANSPOSE || |
| modifier == STATE_MATRIX_INVTRANS) { |
| for (i = 0, row = firstRow; row <= lastRow; row++) { |
| value[i++] = m[row * 4 + 0]; |
| value[i++] = m[row * 4 + 1]; |
| value[i++] = m[row * 4 + 2]; |
| value[i++] = m[row * 4 + 3]; |
| } |
| } |
| else { |
| for (i = 0, row = firstRow; row <= lastRow; row++) { |
| value[i++] = m[row + 0]; |
| value[i++] = m[row + 4]; |
| value[i++] = m[row + 8]; |
| value[i++] = m[row + 12]; |
| } |
| } |
| } |
| return; |
| case STATE_DEPTH_RANGE: |
| value[0] = ctx->Viewport.Near; /* near */ |
| value[1] = ctx->Viewport.Far; /* far */ |
| value[2] = ctx->Viewport.Far - ctx->Viewport.Near; /* far - near */ |
| value[3] = 1.0; |
| return; |
| case STATE_FRAGMENT_PROGRAM: |
| { |
| /* state[1] = {STATE_ENV, STATE_LOCAL} */ |
| /* state[2] = parameter index */ |
| const int idx = (int) state[2]; |
| switch (state[1]) { |
| case STATE_ENV: |
| COPY_4V(value, ctx->FragmentProgram.Parameters[idx]); |
| return; |
| case STATE_LOCAL: |
| COPY_4V(value, ctx->FragmentProgram.Current->Base.LocalParams[idx]); |
| return; |
| default: |
| _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()"); |
| return; |
| } |
| } |
| return; |
| |
| case STATE_VERTEX_PROGRAM: |
| { |
| /* state[1] = {STATE_ENV, STATE_LOCAL} */ |
| /* state[2] = parameter index */ |
| const int idx = (int) state[2]; |
| switch (state[1]) { |
| case STATE_ENV: |
| COPY_4V(value, ctx->VertexProgram.Parameters[idx]); |
| return; |
| case STATE_LOCAL: |
| COPY_4V(value, ctx->VertexProgram.Current->Base.LocalParams[idx]); |
| return; |
| default: |
| _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()"); |
| return; |
| } |
| } |
| return; |
| |
| case STATE_NORMAL_SCALE: |
| ASSIGN_4V(value, ctx->_ModelViewInvScale, 0, 0, 1); |
| return; |
| |
| case STATE_INTERNAL: |
| switch (state[1]) { |
| case STATE_CURRENT_ATTRIB: |
| { |
| const GLuint idx = (GLuint) state[2]; |
| COPY_4V(value, ctx->Current.Attrib[idx]); |
| } |
| return; |
| |
| case STATE_CURRENT_ATTRIB_MAYBE_VP_CLAMPED: |
| { |
| const GLuint idx = (GLuint) state[2]; |
| if(ctx->Light._ClampVertexColor && |
| (idx == VERT_ATTRIB_COLOR0 || |
| idx == VERT_ATTRIB_COLOR1)) { |
| value[0] = CLAMP(ctx->Current.Attrib[idx][0], 0.0f, 1.0f); |
| value[1] = CLAMP(ctx->Current.Attrib[idx][1], 0.0f, 1.0f); |
| value[2] = CLAMP(ctx->Current.Attrib[idx][2], 0.0f, 1.0f); |
| value[3] = CLAMP(ctx->Current.Attrib[idx][3], 0.0f, 1.0f); |
| } |
| else |
| COPY_4V(value, ctx->Current.Attrib[idx]); |
| } |
| return; |
| |
| case STATE_NORMAL_SCALE: |
| ASSIGN_4V(value, |
| ctx->_ModelViewInvScale, |
| ctx->_ModelViewInvScale, |
| ctx->_ModelViewInvScale, |
| 1); |
| return; |
| |
| case STATE_TEXRECT_SCALE: |
| /* Value = { 1/texWidth, 1/texHeight, 0, 1 }. |
| * Used to convert unnormalized texcoords to normalized texcoords. |
| */ |
| { |
| const int unit = (int) state[2]; |
| const struct gl_texture_object *texObj |
| = ctx->Texture.Unit[unit]._Current; |
| if (texObj) { |
| struct gl_texture_image *texImage = texObj->Image[0][0]; |
| ASSIGN_4V(value, |
| (GLfloat) (1.0 / texImage->Width), |
| (GLfloat) (1.0 / texImage->Height), |
| 0.0f, 1.0f); |
| } |
| } |
| return; |
| |
| case STATE_FOG_PARAMS_OPTIMIZED: |
| /* for simpler per-vertex/pixel fog calcs. POW (for EXP/EXP2 fog) |
| * might be more expensive than EX2 on some hw, plus it needs |
| * another constant (e) anyway. Linear fog can now be done with a |
| * single MAD. |
| * linear: fogcoord * -1/(end-start) + end/(end-start) |
| * exp: 2^-(density/ln(2) * fogcoord) |
| * exp2: 2^-((density/(ln(2)^2) * fogcoord)^2) |
| */ |
| value[0] = (ctx->Fog.End == ctx->Fog.Start) |
| ? 1.0f : (GLfloat)(-1.0F / (ctx->Fog.End - ctx->Fog.Start)); |
| value[1] = ctx->Fog.End * -value[0]; |
| value[2] = (GLfloat)(ctx->Fog.Density * M_LOG2E); /* M_LOG2E == 1/ln(2) */ |
| value[3] = (GLfloat)(ctx->Fog.Density * ONE_DIV_SQRT_LN2); |
| return; |
| |
| case STATE_POINT_SIZE_CLAMPED: |
| { |
| /* this includes implementation dependent limits, to avoid |
| * another potentially necessary clamp. |
| * Note: for sprites, point smooth (point AA) is ignored |
| * and we'll clamp to MinPointSizeAA and MaxPointSize, because we |
| * expect drivers will want to say their minimum for AA size is 0.0 |
| * but for non-AA it's 1.0 (because normal points with size below 1.0 |
| * need to get rounded up to 1.0, hence never disappear). GL does |
| * not specify max clamp size for sprites, other than it needs to be |
| * at least as large as max AA size, hence use non-AA size there. |
| */ |
| GLfloat minImplSize; |
| GLfloat maxImplSize; |
| if (ctx->Point.PointSprite) { |
| minImplSize = ctx->Const.MinPointSizeAA; |
| maxImplSize = ctx->Const.MaxPointSize; |
| } |
| else if (ctx->Point.SmoothFlag || ctx->Multisample._Enabled) { |
| minImplSize = ctx->Const.MinPointSizeAA; |
| maxImplSize = ctx->Const.MaxPointSizeAA; |
| } |
| else { |
| minImplSize = ctx->Const.MinPointSize; |
| maxImplSize = ctx->Const.MaxPointSize; |
| } |
| value[0] = ctx->Point.Size; |
| value[1] = ctx->Point.MinSize >= minImplSize ? ctx->Point.MinSize : minImplSize; |
| value[2] = ctx->Point.MaxSize <= maxImplSize ? ctx->Point.MaxSize : maxImplSize; |
| value[3] = ctx->Point.Threshold; |
| } |
| return; |
| case STATE_POINT_SIZE_IMPL_CLAMP: |
| { |
| /* for implementation clamp only in vs */ |
| GLfloat minImplSize; |
| GLfloat maxImplSize; |
| if (ctx->Point.PointSprite) { |
| minImplSize = ctx->Const.MinPointSizeAA; |
| maxImplSize = ctx->Const.MaxPointSize; |
| } |
| else if (ctx->Point.SmoothFlag || ctx->Multisample._Enabled) { |
| minImplSize = ctx->Const.MinPointSizeAA; |
| maxImplSize = ctx->Const.MaxPointSizeAA; |
| } |
| else { |
| minImplSize = ctx->Const.MinPointSize; |
| maxImplSize = ctx->Const.MaxPointSize; |
| } |
| value[0] = ctx->Point.Size; |
| value[1] = minImplSize; |
| value[2] = maxImplSize; |
| value[3] = ctx->Point.Threshold; |
| } |
| return; |
| case STATE_LIGHT_SPOT_DIR_NORMALIZED: |
| { |
| /* here, state[2] is the light number */ |
| /* pre-normalize spot dir */ |
| const GLuint ln = (GLuint) state[2]; |
| COPY_3V(value, ctx->Light.Light[ln]._NormSpotDirection); |
| value[3] = ctx->Light.Light[ln]._CosCutoff; |
| } |
| return; |
| |
| case STATE_LIGHT_POSITION: |
| { |
| const GLuint ln = (GLuint) state[2]; |
| COPY_4V(value, ctx->Light.Light[ln]._Position); |
| } |
| return; |
| |
| case STATE_LIGHT_POSITION_NORMALIZED: |
| { |
| const GLuint ln = (GLuint) state[2]; |
| COPY_4V(value, ctx->Light.Light[ln]._Position); |
| NORMALIZE_3FV( value ); |
| } |
| return; |
| |
| case STATE_LIGHT_HALF_VECTOR: |
| { |
| const GLuint ln = (GLuint) state[2]; |
| GLfloat p[3]; |
| /* Compute infinite half angle vector: |
| * halfVector = normalize(normalize(lightPos) + (0, 0, 1)) |
| * light.EyePosition.w should be 0 for infinite lights. |
| */ |
| COPY_3V(p, ctx->Light.Light[ln]._Position); |
| NORMALIZE_3FV(p); |
| ADD_3V(value, p, ctx->_EyeZDir); |
| NORMALIZE_3FV(value); |
| value[3] = 1.0; |
| } |
| return; |
| |
| case STATE_PT_SCALE: |
| value[0] = ctx->Pixel.RedScale; |
| value[1] = ctx->Pixel.GreenScale; |
| value[2] = ctx->Pixel.BlueScale; |
| value[3] = ctx->Pixel.AlphaScale; |
| return; |
| |
| case STATE_PT_BIAS: |
| value[0] = ctx->Pixel.RedBias; |
| value[1] = ctx->Pixel.GreenBias; |
| value[2] = ctx->Pixel.BlueBias; |
| value[3] = ctx->Pixel.AlphaBias; |
| return; |
| |
| case STATE_SHADOW_AMBIENT: |
| { |
| const int unit = (int) state[2]; |
| const struct gl_texture_object *texObj |
| = ctx->Texture.Unit[unit]._Current; |
| if (texObj) { |
| value[0] = |
| value[1] = |
| value[2] = |
| value[3] = texObj->Sampler.CompareFailValue; |
| } |
| } |
| return; |
| |
| case STATE_FB_SIZE: |
| value[0] = (GLfloat) (ctx->DrawBuffer->Width - 1); |
| value[1] = (GLfloat) (ctx->DrawBuffer->Height - 1); |
| value[2] = 0.0F; |
| value[3] = 0.0F; |
| return; |
| |
| case STATE_FB_WPOS_Y_TRANSFORM: |
| /* A driver may negate this conditional by using ZW swizzle |
| * instead of XY (based on e.g. some other state). */ |
| if (ctx->DrawBuffer->Name != 0) { |
| /* Identity (XY) followed by flipping Y upside down (ZW). */ |
| value[0] = 1.0F; |
| value[1] = 0.0F; |
| value[2] = -1.0F; |
| value[3] = (GLfloat) ctx->DrawBuffer->Height; |
| } else { |
| /* Flipping Y upside down (XY) followed by identity (ZW). */ |
| value[0] = -1.0F; |
| value[1] = (GLfloat) ctx->DrawBuffer->Height; |
| value[2] = 1.0F; |
| value[3] = 0.0F; |
| } |
| return; |
| |
| case STATE_ROT_MATRIX_0: |
| { |
| const int unit = (int) state[2]; |
| GLfloat *rotMat22 = ctx->Texture.Unit[unit].RotMatrix; |
| value[0] = rotMat22[0]; |
| value[1] = rotMat22[2]; |
| value[2] = 0.0; |
| value[3] = 0.0; |
| } |
| return; |
| |
| case STATE_ROT_MATRIX_1: |
| { |
| const int unit = (int) state[2]; |
| GLfloat *rotMat22 = ctx->Texture.Unit[unit].RotMatrix; |
| value[0] = rotMat22[1]; |
| value[1] = rotMat22[3]; |
| value[2] = 0.0; |
| value[3] = 0.0; |
| } |
| return; |
| |
| /* XXX: make sure new tokens added here are also handled in the |
| * _mesa_program_state_flags() switch, below. |
| */ |
| default: |
| /* Unknown state indexes are silently ignored here. |
| * Drivers may do something special. |
| */ |
| return; |
| } |
| return; |
| |
| default: |
| _mesa_problem(ctx, "Invalid state in _mesa_fetch_state"); |
| return; |
| } |
| } |
| |
| |
| /** |
| * Return a bitmask of the Mesa state flags (_NEW_* values) which would |
| * indicate that the given context state may have changed. |
| * The bitmask is used during validation to determine if we need to update |
| * vertex/fragment program parameters (like "state.material.color") when |
| * some GL state has changed. |
| */ |
| GLbitfield |
| _mesa_program_state_flags(const gl_state_index state[STATE_LENGTH]) |
| { |
| switch (state[0]) { |
| case STATE_MATERIAL: |
| case STATE_LIGHT: |
| case STATE_LIGHTMODEL_AMBIENT: |
| case STATE_LIGHTMODEL_SCENECOLOR: |
| case STATE_LIGHTPROD: |
| return _NEW_LIGHT; |
| |
| case STATE_TEXGEN: |
| return _NEW_TEXTURE; |
| case STATE_TEXENV_COLOR: |
| return _NEW_TEXTURE | _NEW_BUFFERS | _NEW_FRAG_CLAMP; |
| |
| case STATE_FOG_COLOR: |
| return _NEW_FOG | _NEW_BUFFERS | _NEW_FRAG_CLAMP; |
| case STATE_FOG_PARAMS: |
| return _NEW_FOG; |
| |
| case STATE_CLIPPLANE: |
| return _NEW_TRANSFORM; |
| |
| case STATE_POINT_SIZE: |
| case STATE_POINT_ATTENUATION: |
| return _NEW_POINT; |
| |
| case STATE_MODELVIEW_MATRIX: |
| return _NEW_MODELVIEW; |
| case STATE_PROJECTION_MATRIX: |
| return _NEW_PROJECTION; |
| case STATE_MVP_MATRIX: |
| return _NEW_MODELVIEW | _NEW_PROJECTION; |
| case STATE_TEXTURE_MATRIX: |
| return _NEW_TEXTURE_MATRIX; |
| case STATE_PROGRAM_MATRIX: |
| return _NEW_TRACK_MATRIX; |
| |
| case STATE_DEPTH_RANGE: |
| return _NEW_VIEWPORT; |
| |
| case STATE_FRAGMENT_PROGRAM: |
| case STATE_VERTEX_PROGRAM: |
| return _NEW_PROGRAM; |
| |
| case STATE_NORMAL_SCALE: |
| return _NEW_MODELVIEW; |
| |
| case STATE_INTERNAL: |
| switch (state[1]) { |
| case STATE_CURRENT_ATTRIB: |
| return _NEW_CURRENT_ATTRIB; |
| case STATE_CURRENT_ATTRIB_MAYBE_VP_CLAMPED: |
| return _NEW_CURRENT_ATTRIB | _NEW_LIGHT | _NEW_BUFFERS; |
| |
| case STATE_NORMAL_SCALE: |
| return _NEW_MODELVIEW; |
| |
| case STATE_TEXRECT_SCALE: |
| case STATE_SHADOW_AMBIENT: |
| case STATE_ROT_MATRIX_0: |
| case STATE_ROT_MATRIX_1: |
| return _NEW_TEXTURE; |
| case STATE_FOG_PARAMS_OPTIMIZED: |
| return _NEW_FOG; |
| case STATE_POINT_SIZE_CLAMPED: |
| case STATE_POINT_SIZE_IMPL_CLAMP: |
| return _NEW_POINT | _NEW_MULTISAMPLE; |
| case STATE_LIGHT_SPOT_DIR_NORMALIZED: |
| case STATE_LIGHT_POSITION: |
| case STATE_LIGHT_POSITION_NORMALIZED: |
| case STATE_LIGHT_HALF_VECTOR: |
| return _NEW_LIGHT; |
| |
| case STATE_PT_SCALE: |
| case STATE_PT_BIAS: |
| return _NEW_PIXEL; |
| |
| case STATE_FB_SIZE: |
| case STATE_FB_WPOS_Y_TRANSFORM: |
| return _NEW_BUFFERS; |
| |
| default: |
| /* unknown state indexes are silently ignored and |
| * no flag set, since it is handled by the driver. |
| */ |
| return 0; |
| } |
| |
| default: |
| _mesa_problem(NULL, "unexpected state[0] in make_state_flags()"); |
| return 0; |
| } |
| } |
| |
| |
| static void |
| append(char *dst, const char *src) |
| { |
| while (*dst) |
| dst++; |
| while (*src) |
| *dst++ = *src++; |
| *dst = 0; |
| } |
| |
| |
| /** |
| * Convert token 'k' to a string, append it onto 'dst' string. |
| */ |
| static void |
| append_token(char *dst, gl_state_index k) |
| { |
| switch (k) { |
| case STATE_MATERIAL: |
| append(dst, "material"); |
| break; |
| case STATE_LIGHT: |
| append(dst, "light"); |
| break; |
| case STATE_LIGHTMODEL_AMBIENT: |
| append(dst, "lightmodel.ambient"); |
| break; |
| case STATE_LIGHTMODEL_SCENECOLOR: |
| break; |
| case STATE_LIGHTPROD: |
| append(dst, "lightprod"); |
| break; |
| case STATE_TEXGEN: |
| append(dst, "texgen"); |
| break; |
| case STATE_FOG_COLOR: |
| append(dst, "fog.color"); |
| break; |
| case STATE_FOG_PARAMS: |
| append(dst, "fog.params"); |
| break; |
| case STATE_CLIPPLANE: |
| append(dst, "clip"); |
| break; |
| case STATE_POINT_SIZE: |
| append(dst, "point.size"); |
| break; |
| case STATE_POINT_ATTENUATION: |
| append(dst, "point.attenuation"); |
| break; |
| case STATE_MODELVIEW_MATRIX: |
| append(dst, "matrix.modelview"); |
| break; |
| case STATE_PROJECTION_MATRIX: |
| append(dst, "matrix.projection"); |
| break; |
| case STATE_MVP_MATRIX: |
| append(dst, "matrix.mvp"); |
| break; |
| case STATE_TEXTURE_MATRIX: |
| append(dst, "matrix.texture"); |
| break; |
| case STATE_PROGRAM_MATRIX: |
| append(dst, "matrix.program"); |
| break; |
| case STATE_MATRIX_INVERSE: |
| append(dst, ".inverse"); |
| break; |
| case STATE_MATRIX_TRANSPOSE: |
| append(dst, ".transpose"); |
| break; |
| case STATE_MATRIX_INVTRANS: |
| append(dst, ".invtrans"); |
| break; |
| case STATE_AMBIENT: |
| append(dst, ".ambient"); |
| break; |
| case STATE_DIFFUSE: |
| append(dst, ".diffuse"); |
| break; |
| case STATE_SPECULAR: |
| append(dst, ".specular"); |
| break; |
| case STATE_EMISSION: |
| append(dst, ".emission"); |
| break; |
| case STATE_SHININESS: |
| append(dst, "lshininess"); |
| break; |
| case STATE_HALF_VECTOR: |
| append(dst, ".half"); |
| break; |
| case STATE_POSITION: |
| append(dst, ".position"); |
| break; |
| case STATE_ATTENUATION: |
| append(dst, ".attenuation"); |
| break; |
| case STATE_SPOT_DIRECTION: |
| append(dst, ".spot.direction"); |
| break; |
| case STATE_SPOT_CUTOFF: |
| append(dst, ".spot.cutoff"); |
| break; |
| case STATE_TEXGEN_EYE_S: |
| append(dst, ".eye.s"); |
| break; |
| case STATE_TEXGEN_EYE_T: |
| append(dst, ".eye.t"); |
| break; |
| case STATE_TEXGEN_EYE_R: |
| append(dst, ".eye.r"); |
| break; |
| case STATE_TEXGEN_EYE_Q: |
| append(dst, ".eye.q"); |
| break; |
| case STATE_TEXGEN_OBJECT_S: |
| append(dst, ".object.s"); |
| break; |
| case STATE_TEXGEN_OBJECT_T: |
| append(dst, ".object.t"); |
| break; |
| case STATE_TEXGEN_OBJECT_R: |
| append(dst, ".object.r"); |
| break; |
| case STATE_TEXGEN_OBJECT_Q: |
| append(dst, ".object.q"); |
| break; |
| case STATE_TEXENV_COLOR: |
| append(dst, "texenv"); |
| break; |
| case STATE_DEPTH_RANGE: |
| append(dst, "depth.range"); |
| break; |
| case STATE_VERTEX_PROGRAM: |
| case STATE_FRAGMENT_PROGRAM: |
| break; |
| case STATE_ENV: |
| append(dst, "env"); |
| break; |
| case STATE_LOCAL: |
| append(dst, "local"); |
| break; |
| /* BEGIN internal state vars */ |
| case STATE_INTERNAL: |
| append(dst, ".internal."); |
| break; |
| case STATE_CURRENT_ATTRIB: |
| append(dst, "current"); |
| break; |
| case STATE_NORMAL_SCALE: |
| append(dst, "normalScale"); |
| break; |
| case STATE_TEXRECT_SCALE: |
| append(dst, "texrectScale"); |
| break; |
| case STATE_FOG_PARAMS_OPTIMIZED: |
| append(dst, "fogParamsOptimized"); |
| break; |
| case STATE_POINT_SIZE_CLAMPED: |
| append(dst, "pointSizeClamped"); |
| break; |
| case STATE_POINT_SIZE_IMPL_CLAMP: |
| append(dst, "pointSizeImplClamp"); |
| break; |
| case STATE_LIGHT_SPOT_DIR_NORMALIZED: |
| append(dst, "lightSpotDirNormalized"); |
| break; |
| case STATE_LIGHT_POSITION: |
| append(dst, "lightPosition"); |
| break; |
| case STATE_LIGHT_POSITION_NORMALIZED: |
| append(dst, "light.position.normalized"); |
| break; |
| case STATE_LIGHT_HALF_VECTOR: |
| append(dst, "lightHalfVector"); |
| break; |
| case STATE_PT_SCALE: |
| append(dst, "PTscale"); |
| break; |
| case STATE_PT_BIAS: |
| append(dst, "PTbias"); |
| break; |
| case STATE_SHADOW_AMBIENT: |
| append(dst, "CompareFailValue"); |
| break; |
| case STATE_FB_SIZE: |
| append(dst, "FbSize"); |
| break; |
| case STATE_FB_WPOS_Y_TRANSFORM: |
| append(dst, "FbWposYTransform"); |
| break; |
| case STATE_ROT_MATRIX_0: |
| append(dst, "rotMatrixRow0"); |
| break; |
| case STATE_ROT_MATRIX_1: |
| append(dst, "rotMatrixRow1"); |
| break; |
| default: |
| /* probably STATE_INTERNAL_DRIVER+i (driver private state) */ |
| append(dst, "driverState"); |
| } |
| } |
| |
| static void |
| append_face(char *dst, GLint face) |
| { |
| if (face == 0) |
| append(dst, "front."); |
| else |
| append(dst, "back."); |
| } |
| |
| static void |
| append_index(char *dst, GLint index) |
| { |
| char s[20]; |
| sprintf(s, "[%d]", index); |
| append(dst, s); |
| } |
| |
| /** |
| * Make a string from the given state vector. |
| * For example, return "state.matrix.texture[2].inverse". |
| * Use free() to deallocate the string. |
| */ |
| char * |
| _mesa_program_state_string(const gl_state_index state[STATE_LENGTH]) |
| { |
| char str[1000] = ""; |
| char tmp[30]; |
| |
| append(str, "state."); |
| append_token(str, state[0]); |
| |
| switch (state[0]) { |
| case STATE_MATERIAL: |
| append_face(str, state[1]); |
| append_token(str, state[2]); |
| break; |
| case STATE_LIGHT: |
| append_index(str, state[1]); /* light number [i]. */ |
| append_token(str, state[2]); /* coefficients */ |
| break; |
| case STATE_LIGHTMODEL_AMBIENT: |
| append(str, "lightmodel.ambient"); |
| break; |
| case STATE_LIGHTMODEL_SCENECOLOR: |
| if (state[1] == 0) { |
| append(str, "lightmodel.front.scenecolor"); |
| } |
| else { |
| append(str, "lightmodel.back.scenecolor"); |
| } |
| break; |
| case STATE_LIGHTPROD: |
| append_index(str, state[1]); /* light number [i]. */ |
| append_face(str, state[2]); |
| append_token(str, state[3]); |
| break; |
| case STATE_TEXGEN: |
| append_index(str, state[1]); /* tex unit [i] */ |
| append_token(str, state[2]); /* plane coef */ |
| break; |
| case STATE_TEXENV_COLOR: |
| append_index(str, state[1]); /* tex unit [i] */ |
| append(str, "color"); |
| break; |
| case STATE_CLIPPLANE: |
| append_index(str, state[1]); /* plane [i] */ |
| append(str, ".plane"); |
| break; |
| case STATE_MODELVIEW_MATRIX: |
| case STATE_PROJECTION_MATRIX: |
| case STATE_MVP_MATRIX: |
| case STATE_TEXTURE_MATRIX: |
| case STATE_PROGRAM_MATRIX: |
| { |
| /* state[0] = modelview, projection, texture, etc. */ |
| /* state[1] = which texture matrix or program matrix */ |
| /* state[2] = first row to fetch */ |
| /* state[3] = last row to fetch */ |
| /* state[4] = transpose, inverse or invtrans */ |
| const gl_state_index mat = state[0]; |
| const GLuint index = (GLuint) state[1]; |
| const GLuint firstRow = (GLuint) state[2]; |
| const GLuint lastRow = (GLuint) state[3]; |
| const gl_state_index modifier = state[4]; |
| if (index || |
| mat == STATE_TEXTURE_MATRIX || |
| mat == STATE_PROGRAM_MATRIX) |
| append_index(str, index); |
| if (modifier) |
| append_token(str, modifier); |
| if (firstRow == lastRow) |
| sprintf(tmp, ".row[%d]", firstRow); |
| else |
| sprintf(tmp, ".row[%d..%d]", firstRow, lastRow); |
| append(str, tmp); |
| } |
| break; |
| case STATE_POINT_SIZE: |
| break; |
| case STATE_POINT_ATTENUATION: |
| break; |
| case STATE_FOG_PARAMS: |
| break; |
| case STATE_FOG_COLOR: |
| break; |
| case STATE_DEPTH_RANGE: |
| break; |
| case STATE_FRAGMENT_PROGRAM: |
| case STATE_VERTEX_PROGRAM: |
| /* state[1] = {STATE_ENV, STATE_LOCAL} */ |
| /* state[2] = parameter index */ |
| append_token(str, state[1]); |
| append_index(str, state[2]); |
| break; |
| case STATE_NORMAL_SCALE: |
| break; |
| case STATE_INTERNAL: |
| append_token(str, state[1]); |
| if (state[1] == STATE_CURRENT_ATTRIB) |
| append_index(str, state[2]); |
| break; |
| default: |
| _mesa_problem(NULL, "Invalid state in _mesa_program_state_string"); |
| break; |
| } |
| |
| return _mesa_strdup(str); |
| } |
| |
| |
| /** |
| * Loop over all the parameters in a parameter list. If the parameter |
| * is a GL state reference, look up the current value of that state |
| * variable and put it into the parameter's Value[4] array. |
| * Other parameter types never change or are explicitly set by the user |
| * with glUniform() or glProgramParameter(), etc. |
| * This would be called at glBegin time. |
| */ |
| void |
| _mesa_load_state_parameters(struct gl_context *ctx, |
| struct gl_program_parameter_list *paramList) |
| { |
| GLuint i; |
| |
| if (!paramList) |
| return; |
| |
| for (i = 0; i < paramList->NumParameters; i++) { |
| if (paramList->Parameters[i].Type == PROGRAM_STATE_VAR) { |
| _mesa_fetch_state(ctx, |
| paramList->Parameters[i].StateIndexes, |
| paramList->ParameterValues[i]); |
| } |
| } |
| } |
| |
| |
| /** |
| * Copy the 16 elements of a matrix into four consecutive program |
| * registers starting at 'pos'. |
| */ |
| static void |
| load_matrix(GLfloat registers[][4], GLuint pos, const GLfloat mat[16]) |
| { |
| GLuint i; |
| for (i = 0; i < 4; i++) { |
| registers[pos + i][0] = mat[0 + i]; |
| registers[pos + i][1] = mat[4 + i]; |
| registers[pos + i][2] = mat[8 + i]; |
| registers[pos + i][3] = mat[12 + i]; |
| } |
| } |
| |
| |
| /** |
| * As above, but transpose the matrix. |
| */ |
| static void |
| load_transpose_matrix(GLfloat registers[][4], GLuint pos, |
| const GLfloat mat[16]) |
| { |
| memcpy(registers[pos], mat, 16 * sizeof(GLfloat)); |
| } |
| |
| |
| /** |
| * Load current vertex program's parameter registers with tracked |
| * matrices (if NV program). This only needs to be done per |
| * glBegin/glEnd, not per-vertex. |
| */ |
| void |
| _mesa_load_tracked_matrices(struct gl_context *ctx) |
| { |
| GLuint i; |
| |
| for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { |
| /* point 'mat' at source matrix */ |
| GLmatrix *mat; |
| if (ctx->VertexProgram.TrackMatrix[i] == GL_MODELVIEW) { |
| mat = ctx->ModelviewMatrixStack.Top; |
| } |
| else if (ctx->VertexProgram.TrackMatrix[i] == GL_PROJECTION) { |
| mat = ctx->ProjectionMatrixStack.Top; |
| } |
| else if (ctx->VertexProgram.TrackMatrix[i] == GL_TEXTURE) { |
| GLuint unit = MIN2(ctx->Texture.CurrentUnit, |
| Elements(ctx->TextureMatrixStack) - 1); |
| mat = ctx->TextureMatrixStack[unit].Top; |
| } |
| else if (ctx->VertexProgram.TrackMatrix[i]==GL_MODELVIEW_PROJECTION_NV) { |
| /* XXX verify the combined matrix is up to date */ |
| mat = &ctx->_ModelProjectMatrix; |
| } |
| else if (ctx->VertexProgram.TrackMatrix[i] >= GL_MATRIX0_NV && |
| ctx->VertexProgram.TrackMatrix[i] <= GL_MATRIX7_NV) { |
| GLuint n = ctx->VertexProgram.TrackMatrix[i] - GL_MATRIX0_NV; |
| ASSERT(n < Elements(ctx->ProgramMatrixStack)); |
| mat = ctx->ProgramMatrixStack[n].Top; |
| } |
| else { |
| /* no matrix is tracked, but we leave the register values as-is */ |
| assert(ctx->VertexProgram.TrackMatrix[i] == GL_NONE); |
| continue; |
| } |
| |
| /* load the matrix values into sequential registers */ |
| if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_IDENTITY_NV) { |
| load_matrix(ctx->VertexProgram.Parameters, i*4, mat->m); |
| } |
| else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_INVERSE_NV) { |
| _math_matrix_analyse(mat); /* update the inverse */ |
| ASSERT(!_math_matrix_is_dirty(mat)); |
| load_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv); |
| } |
| else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_TRANSPOSE_NV) { |
| load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->m); |
| } |
| else { |
| assert(ctx->VertexProgram.TrackMatrixTransform[i] |
| == GL_INVERSE_TRANSPOSE_NV); |
| _math_matrix_analyse(mat); /* update the inverse */ |
| ASSERT(!_math_matrix_is_dirty(mat)); |
| load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv); |
| } |
| } |
| } |