Browse Source

More robust Cg support on non-NVIDIA card + automatically enable hardware skinning in Shader Generator

rdb 10 years ago
parent
commit
ef9908c277

+ 9 - 14
panda/src/display/graphicsStateGuardian.cxx

@@ -814,17 +814,12 @@ dispatch_compute(int num_groups_x, int num_groups_y, int num_groups_z) {
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) GraphicsStateGuardian::
 get_geom_munger(const RenderState *state, Thread *current_thread) {
-  // We can cast the RenderState to a non-const object because we are
-  // only updating a cache within the RenderState, not really changing
-  // any of its properties.
-  RenderState *nc_state = ((RenderState *)state);
-
   // Before we even look up the map, see if the _last_mi value points
   // to this GSG.  This is likely because we tend to visit the same
   // state multiple times during a frame.  Also, this might well be
   // the only GSG in the world anyway.
-  if (!nc_state->_mungers.empty()) {
-    RenderState::Mungers::const_iterator mi = nc_state->_last_mi;
+  if (!state->_mungers.empty()) {
+    RenderState::Mungers::const_iterator mi = state->_last_mi;
     if (!(*mi).first.was_deleted() && (*mi).first == this) {
       if ((*mi).second->is_registered()) {
         return (*mi).second;
@@ -833,23 +828,23 @@ get_geom_munger(const RenderState *state, Thread *current_thread) {
   }
 
   // Nope, we have to look it up in the map.
-  RenderState::Mungers::iterator mi = nc_state->_mungers.find(this);
-  if (mi != nc_state->_mungers.end() && !(*mi).first.was_deleted()) {
+  RenderState::Mungers::iterator mi = state->_mungers.find(this);
+  if (mi != state->_mungers.end() && !(*mi).first.was_deleted()) {
     if ((*mi).second->is_registered()) {
-      nc_state->_last_mi = mi;
+      state->_last_mi = mi;
       return (*mi).second;
     }
     // This GeomMunger is no longer registered.  Remove it from the
     // map.
-    nc_state->_mungers.erase(mi);
+    state->_mungers.erase(mi);
   }
 
   // Nothing in the map; create a new entry.
-  PT(GeomMunger) munger = make_geom_munger(nc_state, current_thread);
+  PT(GeomMunger) munger = make_geom_munger(state, current_thread);
   nassertr(munger != (GeomMunger *)NULL && munger->is_registered(), munger);
 
-  mi = nc_state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first;
-  nc_state->_last_mi = mi;
+  mi = state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first;
+  state->_last_mi = mi;
 
   return munger;
 }

+ 13 - 2
panda/src/display/standardMunger.cxx

@@ -131,7 +131,8 @@ munge_data_impl(const GeomVertexData *data) {
   }
 
   GeomVertexAnimationSpec animation = new_data->get_format()->get_animation();
-  if (_shader_skinning) {
+  if (_shader_skinning || (_auto_shader && hardware_animated_vertices &&
+      !basic_shaders_only && animation.get_animation_type() == AT_panda)) {
     animation.set_hardware(4, true);
 
   } else if (hardware_animated_vertices &&
@@ -387,7 +388,17 @@ munge_state_impl(const RenderState *state) {
     }
     if (shader_state->_generated_shader == NULL) {
       // Cache the generated ShaderAttrib on the shader state.
-      shader_state->_generated_shader = shader_generator->synthesize_shader(shader_state);
+      GeomVertexAnimationSpec spec;
+
+      // Currently we overload this flag to request vertex animation
+      // for the shader generator.
+      const ShaderAttrib *sattr;
+      shader_state->get_attrib_def(sattr);
+      if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) {
+        spec.set_hardware(4, true);
+      }
+
+      shader_state->_generated_shader = shader_generator->synthesize_shader(shader_state, spec);
     }
     munged_state = munged_state->set_attrib(shader_state->_generated_shader);
   }

+ 418 - 71
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -41,6 +41,11 @@ CLP(CgShaderContext)::
 CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) {
   _glgsg = glgsg;
   _cg_program = 0;
+  _glsl_program = 0;
+  _has_divisor = false;
+  _color_attrib_index = CA_color;
+  _transform_table_param = 0;
+  _slider_table_param = 0;
 
   nassertv(s->get_language() == Shader::SL_Cg);
 
@@ -53,6 +58,21 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
     return;
   }
 
+  _transform_table_param = cgGetNamedParameter(_cg_program, "tbl_transforms");
+  if (_transform_table_param) {
+    _transform_table_size = cgGetArraySize(_transform_table_param, 0);
+  }
+
+  _slider_table_param = cgGetNamedParameter(_cg_program, "tbl_sliders");
+  if (_slider_table_param) {
+    _slider_table_size = cgGetArraySize(_slider_table_param, 0);
+  }
+
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "Loading Cg shader " << s->get_filename() << "\n";
+  }
+
   // Load the program.
   if (_cg_program == 0) {
     const char *str = cgGetErrorString(cgGetError());
@@ -73,6 +93,214 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
     }
   }
 
+  if (_cg_program != 0) {
+    if (cgGetProgramProfile(_cg_program) == CG_PROFILE_GLSLC) {
+      _glsl_program = cgGLGetProgramID(_cg_program);
+
+      // Sometimes it fails to link, and Cg fails to propagate the error.
+      GLint link_status;
+      _glgsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &link_status);
+      if (link_status != GL_TRUE) {
+        release_resources();
+      }
+    }
+  }
+
+  if (_cg_program == 0) {
+    return;
+  }
+
+  // We don't use cgGLSetParameterPointer to set the vertex attributes any
+  // longer, since it is buggy on non-NVIDIA hardware and doesn't allow explicit
+  // control over some parameters.  Instead, we have to figure out ourselves how
+  // to map the input varyings to OpenGL vertex attributes.
+  //
+  // We use positive indices to indicate generic vertex attributes, and negative
+  // indices to indicate conventional vertex attributes (ie. glVertexPointer).
+  int nvarying = _shader->_var_spec.size();
+  for (int i = 0; i < nvarying; ++i) {
+    Shader::ShaderVarSpec &bind = _shader->_var_spec[i];
+    CGparameter p = _cg_parameter_map[i];
+    if (p == 0) {
+      bind._id._seqno = CA_unknown;
+      continue;
+    }
+
+    GLint loc = CA_unknown;
+    CGresource res = cgGetParameterResource(p);
+
+    if (cgGetParameterBaseResource(p) == CG_ATTR0) {
+      // The Cg toolkit claims that it is bound to a generic vertex attribute.
+      if (_glsl_program != 0) {
+        // This is where the Cg glslv compiler lies, making the stupid assumption
+        // that we're using an NVIDIA card where generic attributes are aliased
+        // with conventional vertex attributes.  Instead, it always uses
+        // conventional attributes in this case.  Correct this.
+        int index = cgGetParameterResourceIndex(p);
+        switch (index) {
+        case 0:  // gl_Vertex
+          loc = CA_vertex;
+          break;
+        case 2:  // gl_Normal
+          loc = CA_normal;
+          break;
+        case 3:  // gl_Color
+          loc = CA_color;
+          break;
+        case 4:  // gl_SecondaryColor
+          loc = CA_secondary_color;
+          break;
+        case 1:  // glWeightPointerARB?
+        case 5:  // gl_FogCoord
+        case 6:  // PSIZE?
+        case 7:  // BLENDINDICES
+          GLCAT.error()
+            << "Cg varying " << cgGetParameterName(p) << " is bound to "
+               "unrecognized attribute " << index << "\n";
+          loc = CA_unknown;
+          break;
+        default:
+          loc = CA_texcoord + (index - 8);
+          break;
+        }
+      } else {
+        loc = cgGetParameterResourceIndex(p);
+
+        if (loc != 0 && bind._id._name == "vtx_position") {
+          // We really have to bind the vertex position to attribute 0, since
+          // OpenGL will hide the model if attribute 0 is not enabled, and we
+          // can only ever be sure that vtx_position is bound.
+          GLCAT.warning()
+            << "CG varying vtx_position is bound to generic attribute " << loc
+            << "instead of 0.  Use ATTR0 semantic to prevent this.\n";
+        }
+      }
+
+    } else if (res == CG_GLSL_ATTRIB || _glsl_program != 0) {
+      // With cg-glsl-version 130 and higher, no conventional attributes are
+      // used, but it instead uses specially named variables.
+      // A bit of guesswork is involved here; Cg seems to mostly use the
+      // semantics as attribute names in GLSL, with a few exceptions.
+      const char *attribname = NULL;
+      switch (res) {
+      case CG_POSITION0:
+        attribname = "cg_Vertex";
+        break;
+      case CG_NORMAL0:
+        attribname = "NORMAL";
+        break;
+      case CG_COLOR0:
+      case CG_DIFFUSE0:
+        attribname = "COLOR";
+        break;
+      case CG_COLOR1:
+      case CG_SPECULAR0:
+        attribname = "SPECULAR";
+        break;
+      default:
+        // Everything else appears to be named after the semantic string.
+        attribname = cgGetParameterSemantic(p);
+      }
+      loc = _glgsg->_glGetAttribLocation(_glsl_program, attribname);
+
+      if (bind._id._name == "vtx_color") {
+        _color_attrib_index = loc;
+      }
+
+      if (loc == -1) {
+        const char *resource = cgGetParameterResourceName(p);
+        if (!resource) {
+          resource = "unknown";
+        }
+        GLCAT.error()
+          << "Could not find Cg varying " << cgGetParameterName(p);
+        if (attribname) {
+          GLCAT.error(false) << " : " << attribname;
+        }
+        GLCAT.error(false) << " (" << resource << ") in the compiled GLSL program.\n";
+
+      } else if (loc != 0 && bind._id._name == "vtx_position") {
+        // We really have to bind the vertex position to attribute 0, since
+        // OpenGL will hide the model if attribute 0 is not enabled, and we
+        // can only ever be sure that vtx_position is bound.
+        GLCAT.warning()
+          << "CG varying vtx_position is bound to generic attribute " << loc
+          << "instead of 0.  Use ATTR0 semantic to prevent this.\n";
+      }
+
+    } else if (cgGetParameterBaseResource(p) == CG_TEXCOORD0) {
+      // A conventional texture coordinate set.
+      loc = CA_texcoord + cgGetParameterResourceIndex(p);
+
+    } else {
+      // Some other conventional vertex attribute.
+      switch (res) {
+      case CG_POSITION0:
+        loc = CA_vertex;
+        break;
+      case CG_NORMAL0:
+        loc = CA_normal;
+        break;
+      case CG_COLOR0:
+      case CG_DIFFUSE0:
+        loc = CA_color;
+        break;
+      case CG_COLOR1:
+      case CG_SPECULAR0:
+        loc = CA_secondary_color;
+        break;
+      default:
+        GLCAT.error()
+          << "Cg varying " << cgGetParameterName(p);
+        if (cgGetParameterSemantic(p)) {
+          GLCAT.error(false) << " : " << cgGetParameterSemantic(p);
+        }
+        GLCAT.error(false) << " has an unrecognized resource";
+        if (cgGetParameterResourceName(p)) {
+          GLCAT.error(false) << " (" << cgGetParameterResourceName(p) << ")";
+        }
+        GLCAT.error(false) << ".\n";
+        loc = CA_unknown;
+      }
+    }
+
+#ifndef NDEBUG
+    if (GLCAT.is_debug()) {
+      GLCAT.debug()
+        << "Cg varying " << cgGetParameterName(p);
+
+      const char *semantic = cgGetParameterSemantic(p);
+      if (semantic) {
+        GLCAT.debug(false) << " : " << semantic;
+      }
+
+      if (loc == CA_unknown) {
+        GLCAT.debug(false)
+          << " is not bound to a vertex attribute\n";
+
+      } else if (loc >= 0) {
+        GLCAT.debug(false)
+          << " is bound to generic attribute " << loc << "\n";
+
+      } else {
+        const char *resource = cgGetParameterResourceName(p);
+        if (!resource) {
+          resource = "unknown";
+        }
+        GLCAT.debug(false)
+          << " is bound to a conventional attribute (" << resource << ")\n";
+      }
+    }
+    if (loc == CA_unknown) {
+      // Suggest fix to developer.
+      GLCAT.error() << "Try using a different semantic.\n";
+    }
+#endif
+
+    // Abuse the seqno field to store the GLSL attribute location.
+    bind._id._seqno = loc;
+  }
+
   _glgsg->report_my_gl_errors();
 }
 
@@ -385,6 +613,58 @@ issue_parameters(int altered) {
   _glgsg->report_my_gl_errors();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CgGLShaderContext::update_transform_table
+//       Access: Public
+//  Description: Changes the active transform table, used for hardware
+//               skinning.
+////////////////////////////////////////////////////////////////////
+void CLP(CgShaderContext)::
+update_transform_table(const TransformTable *table) {
+  LMatrix4f *matrices = (LMatrix4f *)alloca(_transform_table_size * 64);
+
+  int i = 0;
+  if (table != NULL) {
+    int num_transforms = min(_transform_table_size, (long)table->get_num_transforms());
+    for (; i < num_transforms; ++i) {
+#ifdef STDFLOAT_DOUBLE
+      LMatrix4 matrix;
+      table->get_transform(i)->get_matrix(matrix);
+      matrices[i] = LCAST(float, matrix);
+#else
+      table->get_transform(i)->get_matrix(matrices[i]);
+#endif
+    }
+  }
+  for (; i < _transform_table_size; ++i) {
+    matrices[i] = LMatrix4f::ident_mat();
+  }
+
+  cgGLSetMatrixParameterArrayfc(_transform_table_param, 0,
+                                _transform_table_size, (float *)matrices);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CgGLShaderContext::update_slider_table
+//       Access: Public
+//  Description: Changes the active slider table, used for hardware
+//               skinning.
+////////////////////////////////////////////////////////////////////
+void CLP(CgShaderContext)::
+update_slider_table(const SliderTable *table) {
+  float *sliders = (float *)alloca(_slider_table_size * 4);
+  memset(sliders, 0, _slider_table_size * 4);
+
+  if (table != NULL) {
+    int num_sliders = min(_slider_table_size, (long)table->get_num_sliders());
+    for (int i = 0; i < num_sliders; ++i) {
+      sliders[i] = table->get_slider(i)->get_slider();
+    }
+  }
+
+  cgGLSetParameterArray4f(_slider_table_param, 0, _slider_table_size, sliders);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLCgShaderContext::disable_shader_vertex_arrays
 //       Access: Public
@@ -396,27 +676,35 @@ disable_shader_vertex_arrays() {
     return;
   }
 
-  for (int i=0; i<(int)_shader->_var_spec.size(); i++) {
-    CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno];
-    if (p == 0) continue;
-
-    if (cgGetParameterBaseResource(p) == CG_ATTR0) {
-      int index = cgGetParameterResourceIndex(p);
-      if (index >= 8) {
-        _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8));
-        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+  for (int i = 0; i < (int)_shader->_var_spec.size(); ++i) {
+    GLint p = _shader->_var_spec[i]._id._seqno;
 
-      } else if (index == 0) {
+    if (p >= 0) {
+      _glgsg->_glDisableVertexAttribArray(p);
+      if (_has_divisor) {
+        _glgsg->_glVertexAttribDivisor(p, 0);
+      }
+    } else {
+      switch (p) {
+      case CA_unknown:
+        break;
+      case CA_vertex:
         glDisableClientState(GL_VERTEX_ARRAY);
-
-      } else if (index == 2) {
+        break;
+      case CA_normal:
         glDisableClientState(GL_NORMAL_ARRAY);
-
-      } else if (index == 3) {
+        break;
+      case CA_color:
         glDisableClientState(GL_COLOR_ARRAY);
+        break;
+      case CA_secondary_color:
+        glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
+        break;
+      default:
+        _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (p - CA_texcoord));
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+        break;
       }
-    } else {
-      cgGLDisableClientState(p);
     }
   }
 
@@ -458,12 +746,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
     int start, stride, num_values;
     int nvarying = _shader->_var_spec.size();
     for (int i = 0; i < nvarying; ++i) {
-      if (_cg_parameter_map[_shader->_var_spec[i]._id._seqno] == 0) {
-        continue;
-      }
-
-      InternalName *name = _shader->_var_spec[i]._name;
-      int texslot = _shader->_var_spec[i]._append_uv;
+      const Shader::ShaderVarSpec &bind = _shader->_var_spec[i];
+      InternalName *name = bind._name;
+      int texslot = bind._append_uv;
       if (texslot >= 0 && texslot < _glgsg->_state_texture->get_num_on_stages()) {
         TextureStage *stage = _glgsg->_state_texture->get_on_stage(texslot);
         InternalName *texname = stage->get_texcoord_name();
@@ -474,84 +759,146 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
           name = name->append(texname->get_basename());
         }
       }
-      if (_glgsg->_data_reader->get_array_info(name,
-                                               array_reader, num_values, numeric_type,
-                                               start, stride)) {
+      GLint p = bind._id._seqno;
+
+      // Don't apply vertex colors if they are disabled with a ColorAttrib.
+      int num_elements, element_stride, divisor;
+      bool normalized;
+      if ((p != _color_attrib_index || _glgsg->_vertex_colors_enabled) &&
+          _glgsg->_data_reader->get_array_info(name, array_reader,
+                                               num_values, numeric_type,
+                                               normalized, start, stride, divisor,
+                                               num_elements, element_stride)) {
         const unsigned char *client_pointer;
         if (!_glgsg->setup_array_data(client_pointer, array_reader, force)) {
           return false;
         }
+        client_pointer += start;
+
+        // We don't use cgGLSetParameterPointer because it is very buggy and
+        // limited in the options we can set.start
+        GLenum type = _glgsg->get_numeric_type(numeric_type);
+        if (p >= 0) {
+          _glgsg->_glEnableVertexAttribArray(p);
+
+          if (bind._integer) {
+            _glgsg->_glVertexAttribIPointer(p, num_values, type,
+                                            stride, client_pointer);
+          } else if (numeric_type == GeomEnums::NT_packed_dabc) {
+            // GL_BGRA is a special accepted value available since OpenGL 3.2.
+            // It requires us to pass GL_TRUE for normalized.
+            _glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE,
+                                           GL_TRUE, stride, client_pointer);
+          } else {
+            _glgsg->_glVertexAttribPointer(p, num_values, type,
+                                           normalized, stride, client_pointer);
+          }
 
-        CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno];
+          if (_glgsg->_supports_vertex_attrib_divisor) {
+            _glgsg->_glVertexAttribDivisor(p, divisor);
+            _has_divisor = true;
+          }
 
-        if (numeric_type == GeomEnums::NT_packed_dabc) {
-          // Yes, this is a thing.
-          num_values = GL_BGRA;
-        }
+        } else {
+          // It's a conventional vertex attribute.  Ugh.
+          switch (p) {
+          case CA_unknown:
+            break;
 
-        // cgGLSetParameterPointer is just stupidly bugged on every level.
-        // Sigh.  This seems to work on both NVIDIA and AMD cards now.
-        if (cgGetParameterBaseResource(p) == CG_ATTR0) {
-          int index = cgGetParameterResourceIndex(p);
-          switch (index) {
-          case 0:  // gl_Vertex
-            glVertexPointer(num_values, _glgsg->get_numeric_type(numeric_type),
-                            stride, client_pointer + start);
+          case CA_vertex:
+            glVertexPointer(num_values, type, stride, client_pointer);
             glEnableClientState(GL_VERTEX_ARRAY);
             break;
 
-          case 2:  // gl_Normal
-            glNormalPointer(_glgsg->get_numeric_type(numeric_type),
-                            stride, client_pointer + start);
+          case CA_normal:
+            glNormalPointer(type, stride, client_pointer);
             glEnableClientState(GL_NORMAL_ARRAY);
             break;
 
-          case 3:  // gl_Color
-            glColorPointer(num_values, _glgsg->get_numeric_type(numeric_type),
-                           stride, client_pointer + start);
+          case CA_color:
+            glColorPointer(num_values, type, stride, client_pointer);
             glEnableClientState(GL_COLOR_ARRAY);
             break;
 
-          case 4:  // gl_SecondaryColor
-            //glSecondaryColorPointer(num_values, _glgsg->get_numeric_type(numeric_type),
-            //                        stride, client_pointer + start);
-            //glEnableClientState(GL_SECONDARY_COLOR_ARRAY);
-            //break;
-          case 5:  // gl_FogCoord
-          case 6:  // PSIZE?
-          case 7:  // BLENDINDICES?
-          case 1:  // glWeightPointerARB?
-            GLCAT.error()
-              << "Unable to bind " << *name << " to "
-              << cgGetParameterResourceName(p) << "\n";
+          case CA_secondary_color:
+            _glgsg->_glSecondaryColorPointer(num_values, type,
+                                             stride, client_pointer);
+            glEnableClientState(GL_SECONDARY_COLOR_ARRAY);
             break;
 
           default:
-            _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8));
-            glTexCoordPointer(num_values, _glgsg->get_numeric_type(numeric_type),
-                              stride, client_pointer + start);
+            _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (p - CA_texcoord));
+            glTexCoordPointer(num_values, type, stride, client_pointer);
             glEnableClientState(GL_TEXTURE_COORD_ARRAY);
             break;
           }
+        }
+      } else {
+        // There is no vertex column with this name; disable the attribute array.
+        if (p == 0) {
+          //NOTE: if we disable attribute 0 in compatibility profile, the object
+          // will disappear.  In GLSL we fix this by forcing the vertex column
+          // to be at 0, but we don't have control over that with Cg.  So, we
+          // work around this by just binding something silly to 0.
+          // This breaks flat colors, but it's better than invisible objects?
+          _glgsg->_glEnableVertexAttribArray(0);
+          if (bind._integer) {
+            _glgsg->_glVertexAttribIPointer(0, 4, GL_INT, 0, 0);
+          } else {
+            _glgsg->_glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
+          }
+
+        } else if (p > 0) {
+          _glgsg->_glDisableVertexAttribArray(p);
+
+          if (p == _color_attrib_index) {
+#ifdef STDFLOAT_DOUBLE
+            _glgsg->_glVertexAttrib4dv(p, _glgsg->_scene_graph_color.get_data());
+#else
+            _glgsg->_glVertexAttrib4fv(p, _glgsg->_scene_graph_color.get_data());
+#endif
+          }
         } else {
-          if (name == InternalName::get_normal() && num_values == 4) {
-            // In some cases, the normals are aligned to 4 values.  We tell
-            // it to use three values exactly, otherwise we get the error:
-            // An unsupported GL extension was required to perform this operation.
-            num_values = 3;
+          switch (p) {
+          case CA_unknown:
+            break;
+          case CA_vertex:
+            glDisableClientState(GL_VERTEX_ARRAY);
+            break;
+          case CA_normal:
+            glDisableClientState(GL_NORMAL_ARRAY);
+            break;
+          case CA_color:
+            glDisableClientState(GL_COLOR_ARRAY);
+#ifdef STDFLOAT_DOUBLE
+            glColor4dv(_glgsg->_scene_graph_color.get_data());
+#else
+            glColor4fv(_glgsg->_scene_graph_color.get_data());
+#endif
+            break;
+          case CA_secondary_color:
+            glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
+            break;
+          default:
+            _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (p - CA_texcoord));
+            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+            break;
           }
-          cgGLSetParameterPointer(p, num_values,
-                                  _glgsg->get_numeric_type(numeric_type),
-                                  stride, client_pointer + start);
-          cgGLEnableClientState(p);
         }
-      } else {
-        CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno];
-        cgGLDisableClientState(p);
       }
     }
   }
 
+  if (_transform_table_param) {
+    const TransformTable *table = _glgsg->_data_reader->get_transform_table();
+    update_transform_table(table);
+  }
+
+  if (_slider_table_param) {
+    const SliderTable *table = _glgsg->_data_reader->get_slider_table();
+    update_slider_table(table);
+  }
+
   cg_report_errors();
   _glgsg->report_my_gl_errors();
 

+ 21 - 0
panda/src/glstuff/glCgShaderContext_src.h

@@ -41,6 +41,8 @@ public:
   void bind(bool reissue_parameters = true);
   void unbind();
   void issue_parameters(int altered);
+  void update_transform_table(const TransformTable *table);
+  void update_slider_table(const SliderTable *table);
   void disable_shader_vertex_arrays();
   bool update_shader_vertex_arrays(ShaderContext *prev, bool force);
   void disable_shader_texture_bindings();
@@ -50,13 +52,32 @@ public:
   INLINE bool uses_custom_vertex_arrays(void);
   INLINE bool uses_custom_texture_bindings(void);
 
+  // Special values for location to indicate conventional attrib slots.
+  enum ConventionalAttrib {
+    CA_unknown = -1,
+    CA_vertex = -2,
+    CA_normal = -3,
+    CA_color = -4,
+    CA_secondary_color = -5,
+    CA_texcoord = -32,
+  };
+
 private:
   CGprogram _cg_program;
+  GLuint _glsl_program;
+
+  GLint _color_attrib_index;
+  CGparameter _transform_table_param;
+  CGparameter _slider_table_param;
+  long _transform_table_size;
+  long _slider_table_size;
 
   pvector<CGparameter> _cg_parameter_map;
 
   CLP(GraphicsStateGuardian) *_glgsg;
 
+  bool _has_divisor;
+
   void release_resources();
 
 public:

+ 12 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -599,6 +599,17 @@ reset() {
   }
 #endif
 
+#ifndef OPENGLES
+  if (is_at_least_gl_version(1, 4)) {
+    _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)
+      get_extension_func("glSecondaryColorPointer");
+
+  } else if (has_extension("GL_EXT_secondary_color")) {
+    _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)
+      get_extension_func("glSecondaryColorPointerEXT");
+  }
+#endif
+
 #ifdef OPENGLES_2
   _supports_vertex_blend = false;
 #else
@@ -7121,6 +7132,7 @@ get_extension_func(const char *name) {
 #endif
 #ifdef EXPECT_GL_VERSION_1_4
     { "glPointParameterfv", (void *)&glPointParameterfv },
+    { "glSecondaryColorPointer", (void *)&glSecondaryColorPointer },
 #endif
 #ifdef EXPECT_GL_VERSION_1_5
     { "glBeginQuery", (void *)&glBeginQuery },

+ 5 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -103,6 +103,7 @@ typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data);
 typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face);
+typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
 typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
 typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count);
 typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights);
@@ -662,6 +663,10 @@ public:
   bool _explicit_primitive_restart;
 #endif
 
+#ifndef OPENGLES
+  PFNGLSECONDARYCOLORPOINTERPROC _glSecondaryColorPointer;
+#endif
+
   bool _supports_vertex_blend;
   PFNGLWEIGHTPOINTERARBPROC _glWeightPointer;
   PFNGLVERTEXBLENDARBPROC _glVertexBlend;

+ 1 - 1
panda/src/gobj/shader.I

@@ -49,7 +49,7 @@ get_filename(const ShaderType &type) const {
 
   } else {
     // Um, better than nothing?
-    return _filename._vertex;
+    return _filename._fragment;
   }
 }
 

+ 97 - 100
panda/src/gobj/shader.cxx

@@ -553,6 +553,7 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type,
       ShaderArgClass     arg_subclass = arg_class;
 
       CGenum vbl = cgGetParameterVariability(parameter);
+      CGtype base_type = cgGetParameterBaseType(parameter);
 
       if ((vbl==CG_VARYING)||(vbl==CG_UNIFORM)) {
         switch (cgGetParameterType(parameter)) {
@@ -567,16 +568,28 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type,
 
             arg_dim[0]  = cgGetArraySize(parameter, 0);
 
+            // Fall through
           default: {
             arg_dim[1] = cgGetParameterRows(parameter);
             arg_dim[2] = cgGetParameterColumns(parameter);
 
-            ShaderArgId id;
-            id._name = cgGetParameterName(parameter);
-            id._type  = type;
-            id._seqno = -1;
-            success &= compile_parameter(id, arg_class, arg_subclass, arg_type,
-                arg_dir, (vbl == CG_VARYING), arg_dim, shader_cat.get_safe_ptr()); break;
+            ShaderArgInfo p;
+            p._id._name   = cgGetParameterName(parameter);
+            p._id._type   = type;
+            p._id._seqno  = -1;
+            p._class      = arg_class;
+            p._subclass   = arg_subclass;
+            p._type       = arg_type;
+            p._direction  = arg_dir;
+            p._varying    = (vbl == CG_VARYING);
+            p._integer    = (base_type == CG_UINT || base_type == CG_INT ||
+                             base_type == CG_ULONG || base_type == CG_LONG ||
+                             base_type == CG_USHORT || base_type == CG_SHORT ||
+                             base_type == CG_UCHAR || base_type == CG_CHAR);
+            p._cat        = shader_cat.get_safe_ptr();
+
+            success &= compile_parameter(p, arg_dim);
+            break;
           }
         }
       }
@@ -601,24 +614,7 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type,
 //               an error message onto the error messages.
 ////////////////////////////////////////////////////////////////////
 bool Shader::
-compile_parameter(const ShaderArgId        &arg_id,
-                  const ShaderArgClass     &arg_class,
-                  const ShaderArgClass     &arg_subclass,
-                  const ShaderArgType      &arg_type,
-                  const ShaderArgDir       &arg_direction,
-                  bool                      arg_varying,
-                  int                      *arg_dim,
-                  NotifyCategory           *arg_cat)
-{
-  ShaderArgInfo p;
-  p._id         = arg_id;
-  p._class      = arg_class;
-  p._subclass   = arg_subclass;
-  p._type       = arg_type;
-  p._direction  = arg_direction;
-  p._varying    = arg_varying;
-  p._cat        = arg_cat;
-
+compile_parameter(ShaderArgInfo &p, int *arg_dim) {
   if (p._id._name.size() == 0) return true;
   if (p._id._name[0] == '$') return true;
 
@@ -650,9 +646,9 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderVarSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._append_uv = -1;
-    bind._integer = false;
+    bind._integer = p._integer;
 
     if (pieces.size() == 2) {
       if (pieces[1] == "position") {
@@ -685,6 +681,24 @@ compile_parameter(const ShaderArgId        &arg_id,
         _var_spec.push_back(bind);
         return true;
       }
+    } else if (pieces.size() == 3) {
+      if (pieces[1] == "transform") {
+        if (pieces[2] == "blend") {
+          bind._name = InternalName::get_transform_blend();
+          _var_spec.push_back(bind);
+          return true;
+        }
+        if (pieces[2] == "index") {
+          bind._name = InternalName::get_transform_index();
+          _var_spec.push_back(bind);
+          return true;
+        }
+        if (pieces[2] == "weight") {
+          bind._name = InternalName::get_transform_weight();
+          _var_spec.push_back(bind);
+          return true;
+        }
+      }
     }
 
     bind._name = InternalName::get_root();
@@ -793,7 +807,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
 
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._func = SMF_compose;
 
     int next = 1;
@@ -856,7 +870,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       if (!cp_errchk_parameter_float(p,16,16)) {
         return false;
       }
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._piece = SMP_transpose;
       bind._func = SMF_first;
       bind._part[0] = SMO_attr_material;
@@ -867,7 +881,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       if (!cp_errchk_parameter_float(p,3,4)) {
         return false;
       }
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._piece = SMP_row3;
       bind._func = SMF_first;
       bind._part[0] = SMO_attr_color;
@@ -878,7 +892,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       if (!cp_errchk_parameter_float(p,3,4)) {
         return false;
       }
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._piece = SMP_row3;
       bind._func = SMF_first;
       bind._part[0] = SMO_attr_colorscale;
@@ -889,7 +903,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       if (!cp_errchk_parameter_float(p,3,4)) {
         return false;
       }
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._piece = SMP_row3;
       bind._func = SMF_first;
       bind._part[0] = SMO_attr_fog;
@@ -900,7 +914,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       if (!cp_errchk_parameter_float(p,3,4)) {
         return false;
       }
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._piece = SMP_row3;
       bind._func = SMF_first;
       bind._part[0] = SMO_attr_fogcolor;
@@ -940,7 +954,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[0] = SMO_alight_x;
@@ -961,7 +975,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[0] = SMO_satten_x;
@@ -981,7 +995,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_transpose;
     int next = 1;
     pieces.push_back("");
@@ -1023,7 +1037,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_whole;
     bind._func = SMF_first;
     bind._part[0] = SMO_texmat_x;
@@ -1044,7 +1058,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[0] = SMO_plane_x;
@@ -1065,7 +1079,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[0] = SMO_clipplane_x;
@@ -1087,7 +1101,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[1] = SMO_identity;
@@ -1136,7 +1150,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderTexSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._name = 0;
     bind._stage = atoi(pieces[1].c_str());
     switch (p._type) {
@@ -1168,7 +1182,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[0] = SMO_texpad_x;
@@ -1188,7 +1202,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
     }
     ShaderMatSpec bind;
-    bind._id = arg_id;
+    bind._id = p._id;
     bind._piece = SMP_row3;
     bind._func = SMF_first;
     bind._part[0] = SMO_texpix_x;
@@ -1200,6 +1214,11 @@ compile_parameter(const ShaderArgId        &arg_id,
     return true;
   }
 
+  if (pieces[0] == "tbl") {
+    // Handled elsewhere.
+    return true;
+  }
+
   if (pieces[0] == "l") {
     // IMPLEMENT THE ERROR CHECKING
     return true; // Cg handles this automatically.
@@ -1235,7 +1254,7 @@ compile_parameter(const ShaderArgId        &arg_id,
       return false;
 
     ShaderPtrSpec bind;
-    bind._id      = arg_id;
+    bind._id      = p._id;
     bind._arg     = kinputname;
     bind._info    = p;
     bind._dep[0]  = SSD_general | SSD_shaderinputs;
@@ -1253,7 +1272,7 @@ compile_parameter(const ShaderArgId        &arg_id,
     switch (p._type) {
     case SAT_sampler1d: {
       ShaderTexSpec bind;
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._name = kinputname;
       bind._desired_type = Texture::TT_1d_texture;
       _tex_spec.push_back(bind);
@@ -1261,7 +1280,7 @@ compile_parameter(const ShaderArgId        &arg_id,
     }
     case SAT_sampler2d: {
       ShaderTexSpec bind;
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._name = kinputname;
       bind._desired_type = Texture::TT_2d_texture;
       _tex_spec.push_back(bind);
@@ -1269,7 +1288,7 @@ compile_parameter(const ShaderArgId        &arg_id,
     }
     case SAT_sampler3d: {
       ShaderTexSpec bind;
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._name = kinputname;
       bind._desired_type = Texture::TT_3d_texture;
       _tex_spec.push_back(bind);
@@ -1277,7 +1296,7 @@ compile_parameter(const ShaderArgId        &arg_id,
     }
     case SAT_sampler2dArray: {
       ShaderTexSpec bind;
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._name = kinputname;
       bind._desired_type = Texture::TT_2d_texture_array;
       _tex_spec.push_back(bind);
@@ -1285,7 +1304,7 @@ compile_parameter(const ShaderArgId        &arg_id,
     }
     case SAT_samplercube: {
       ShaderTexSpec bind;
-      bind._id = arg_id;
+      bind._id = p._id;
       bind._name = kinputname;
       bind._desired_type = Texture::TT_cube_map;
       _tex_spec.push_back(bind);
@@ -1385,7 +1404,8 @@ cg_parameter_type(CGparameter p) {
     default: return SAT_unknown;
     }
   case CG_PARAMETERCLASS_ARRAY: return SAT_unknown;
-  default: return SAT_unknown;
+  default:
+    return SAT_unknown;
   }
 }
 
@@ -1531,6 +1551,12 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps,
     if (shader_cat.is_debug()) {
       shader_cat.debug()
         << "Compilation with active profile failed: " << cgGetErrorString(err) << "\n";
+      if (err == CG_COMPILER_ERROR) {
+        const char *listing = cgGetLastListing(context);
+        if (listing != NULL) {
+          shader_cat.debug(false) << listing;
+        }
+      }
     }
   }
 
@@ -1632,19 +1658,19 @@ cg_compile_shader(const ShaderCaps &caps, CGcontext context) {
       shader_cat.debug()
         << "Cg vertex profile: " << cgGetProfileString((CGprofile)_cg_vprofile) << "\n";
       vertex_program = cgGetProgramString(_cg_vprogram, CG_COMPILED_PROGRAM);
-      shader_cat.debug() << vertex_program << "\n";
+      shader_cat.spam() << vertex_program << "\n";
     }
     if (_cg_fprogram != 0) {
       shader_cat.debug()
         << "Cg fragment profile: " << cgGetProfileString((CGprofile)_cg_fprofile) << "\n";
       fragment_program = cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM);
-      shader_cat.debug() << fragment_program << "\n";
+      shader_cat.spam() << fragment_program << "\n";
     }
     if (_cg_gprogram != 0) {
       shader_cat.debug()
         << "Cg geometry profile: " << cgGetProfileString((CGprofile)_cg_gprofile) << "\n";
       geometry_program = cgGetProgramString(_cg_gprogram, CG_COMPILED_PROGRAM);
-      shader_cat.debug() << geometry_program << "\n";
+      shader_cat.spam() << geometry_program << "\n";
     }
   }
 
@@ -1745,17 +1771,18 @@ cg_analyze_shader(const ShaderCaps &caps) {
     }
   }
 
-  // Assign sequence numbers to all parameters.
+  // Assign sequence numbers to all parameters.  GLCgShaderContext relies
+  // on the fact that the varyings start at seqno 0.
   int seqno = 0;
+  for (int i=0; i<(int)_var_spec.size(); i++) {
+    _var_spec[i]._id._seqno = seqno++;
+  }
   for (int i=0; i<(int)_mat_spec.size(); i++) {
     _mat_spec[i]._id._seqno = seqno++;
   }
   for (int i=0; i<(int)_tex_spec.size(); i++) {
     _tex_spec[i]._id._seqno = seqno++;
   }
-  for (int i=0; i<(int)_var_spec.size(); i++) {
-    _var_spec[i]._id._seqno = seqno++;
-  }
 
   for (int i=0; i<(int)_ptr_spec.size(); i++) {
     _ptr_spec[i]._id._seqno = seqno++;
@@ -1955,7 +1982,7 @@ cg_compile_for(const ShaderCaps &caps, CGcontext context,
       const char *resource = cgGetParameterResourceName(p);
       if (resource != NULL) {
         shader_cat.debug() << "Texture parameter " << id._name
-                         << " is bound to resource " << resource << "\n";
+                          << " is bound to resource " << resource << "\n";
       }
     }
     map[id._seqno] = p;
@@ -1967,48 +1994,18 @@ cg_compile_for(const ShaderCaps &caps, CGcontext context,
 
     const char *resource = cgGetParameterResourceName(p);
     if (shader_cat.is_debug() && resource != NULL) {
-      shader_cat.debug()
-        << "Varying parameter " << id._name << " is bound to resource "
-        << cgGetParameterResourceName(p) << "\n";
-    }
-
-    if (cgGetParameterBaseResource(p) == CG_UNDEFINED) {
-      // I really don't know what this means, but it happens when I
-      // use the NORMAL0 semantic instead of NORMAL, or POSITION0
-      // instead of POSITION, etc.  Not catching this results in a
-      // continuous stream of errors at the renderer side.
-      shader_cat.error()
-        << "Varying parameter " << id._name;
-
-      const char *semantic = cgGetParameterSemantic(p);
-      if (semantic != NULL) {
-        shader_cat.error(false) << " : " << semantic;
-      }
-      if (resource != NULL) {
-        shader_cat.error(false) << " (bound to resource " << resource << ")";
-      }
-      shader_cat.error(false) << " is invalid!\n";
-
-#ifndef NDEBUG
-      // Let's try to give the developer a hint...
-      if (semantic != NULL) {
-        if (strcmp(semantic, "POSITION0") == 0) {
-          shader_cat.error() << "Try using the semantic POSITION instead of POSITION0.\n";
-        } else if (strcmp(semantic, "NORMAL0") == 0) {
-          shader_cat.error() << "Try using the semantic NORMAL instead of NORMAL0.\n";
-        } else if (strcmp(semantic, "DIFFUSE0") == 0) {
-          shader_cat.error() << "Try using the semantic DIFFUSE instead of DIFFUSE0.\n";
-        } else if (strcmp(semantic, "SPECULAR0") == 0) {
-          shader_cat.error() << "Try using the semantic SPECULAR instead of SPECULAR0.\n";
-        } else if (strcmp(semantic, "FOGCOORD0") == 0) {
-          shader_cat.error() << "Try using the semantic FOGCOORD instead of FOGCOORD0.\n";
-        } else if (strcmp(semantic, "PSIZE0") == 0) {
-          shader_cat.error() << "Try using the semantic PSIZE instead of PSIZE0.\n";
-        }
+      if (cgGetParameterResource(p) == CG_GLSL_ATTRIB) {
+        shader_cat.debug()
+          << "Varying parameter " << id._name << " is bound to GLSL attribute "
+          << resource << "\n";
+      } else {
+        shader_cat.debug()
+          << "Varying parameter " << id._name << " is bound to resource "
+          << resource << " (" << cgGetParameterResource(p)
+          << ", index " << cgGetParameterResourceIndex(p) << ")\n";
       }
-#endif  // NDEBUG
-      p = 0;
     }
+
     map[id._seqno] = p;
   }
 
@@ -2059,9 +2056,9 @@ Shader(ShaderLanguage lang) :
   _cg_fprofile = CG_PROFILE_UNKNOWN;
   _cg_gprofile = CG_PROFILE_UNKNOWN;
   if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) {
-    _default_caps._active_vprofile = CG_PROFILE_UNKNOWN;
-    _default_caps._active_fprofile = CG_PROFILE_UNKNOWN;
-    _default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
+    _default_caps._active_vprofile = CG_PROFILE_GENERIC;
+    _default_caps._active_fprofile = CG_PROFILE_GENERIC;
+    _default_caps._active_gprofile = CG_PROFILE_GENERIC;
     _default_caps._ultimate_vprofile = cgGetProfile("glslv");
     _default_caps._ultimate_fprofile = cgGetProfile("glslf");
     _default_caps._ultimate_gprofile = cgGetProfile("glslg");

+ 2 - 8
panda/src/gobj/shader.h

@@ -297,6 +297,7 @@ public:
     ShaderArgType     _type;
     ShaderArgDir      _direction;
     bool              _varying;
+    bool              _integer;
     NotifyCategory   *_cat;
   };
 
@@ -478,14 +479,7 @@ public:
                           bool &success);
 #endif
 
-  bool compile_parameter(const ShaderArgId        &arg_id,
-                         const ShaderArgClass     &arg_class,
-                         const ShaderArgClass     &arg_subclass,
-                         const ShaderArgType      &arg_type,
-                         const ShaderArgDir       &arg_direction,
-                         bool                      arg_varying,
-                         int                      *arg_dim,
-                         NotifyCategory           *arg_cat);
+  bool compile_parameter(ShaderArgInfo &p, int *arg_dim);
 
   void clear_parameters();
 

+ 12 - 0
panda/src/pgraph/cullableObject.cxx

@@ -18,6 +18,7 @@
 #include "colorAttrib.h"
 #include "texGenAttrib.h"
 #include "textureAttrib.h"
+#include "shaderAttrib.h"
 #include "renderState.h"
 #include "clockObject.h"
 #include "cullTraverser.h"
@@ -128,6 +129,17 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       return false;
     }
 
+    // If we have prepared it for skinning via the shader generator,
+    // mark a flag on the state so that the shader generator will do this.
+    // We should probably find a cleaner way to do this.
+    const ShaderAttrib *sattr;
+    if (_state->get_attrib(sattr) && sattr->auto_shader()) {
+      GeomVertexDataPipelineReader data_reader(_munged_data, current_thread);
+      if (data_reader.get_format()->get_animation().get_animation_type() == Geom::AT_hardware) {
+        _state = _state->set_attrib(sattr->set_flag(ShaderAttrib::F_hardware_skinning, true));
+      }
+    }
+
     StateMunger *state_munger;
     DCAST_INTO_R(state_munger, munger, false);
     _state = state_munger->munge_state(_state);

+ 2 - 2
panda/src/pgraph/renderState.h

@@ -271,8 +271,8 @@ private:
   // The code to manage this map lives in
   // GraphicsStateGuardian::get_geom_munger().
   typedef pmap<WCPT(GraphicsStateGuardianBase), PT(GeomMunger) > Mungers;
-  Mungers _mungers;
-  Mungers::const_iterator _last_mi;
+  mutable Mungers _mungers;
+  mutable Mungers::const_iterator _last_mi;
 
   // This is used to mark nodes as we visit them to detect cycles.
   UpdateSeq _cycle_detect;

+ 1 - 0
panda/src/pgraph/shaderAttrib.cxx

@@ -702,6 +702,7 @@ get_auto_shader_attrib_impl(const RenderState *state) const {
   attrib->_auto_gloss_on = _auto_gloss_on;
   attrib->_auto_ramp_on = _auto_ramp_on;
   attrib->_auto_shadow_on = _auto_shadow_on;
+  attrib->_flags = _flags;
   return return_new(attrib);
 }
 

+ 60 - 14
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -92,19 +92,25 @@ reset_register_allocator() {
 ////////////////////////////////////////////////////////////////////
 const char *ShaderGenerator::
 alloc_vreg() {
+  // The ATTR# input sseem to map to generic vertex attributes in
+  // both arbvp1 and glslv, which behave more consistently.
   switch (_vtregs_used) {
-  case  0: _vtregs_used += 1; return "TEXCOORD0";
-  case  1: _vtregs_used += 1; return "TEXCOORD1";
-  case  2: _vtregs_used += 1; return "TEXCOORD2";
-  case  3: _vtregs_used += 1; return "TEXCOORD3";
-  case  4: _vtregs_used += 1; return "TEXCOORD4";
-  case  5: _vtregs_used += 1; return "TEXCOORD5";
-  case  6: _vtregs_used += 1; return "TEXCOORD6";
-  case  7: _vtregs_used += 1; return "TEXCOORD7";
+  case  0: _vtregs_used += 1; return "ATTR8";
+  case  1: _vtregs_used += 1; return "ATTR9";
+  case  2: _vtregs_used += 1; return "ATTR10";
+  case  3: _vtregs_used += 1; return "ATTR11";
+  case  4: _vtregs_used += 1; return "ATTR12";
+  case  5: _vtregs_used += 1; return "ATTR13";
+  case  6: _vtregs_used += 1; return "ATTR14";
+  case  7: _vtregs_used += 1; return "ATTR15";
   }
   switch (_vcregs_used) {
-  case  0: _vcregs_used += 1; return "COLOR0";
-  case  1: _vcregs_used += 1; return "COLOR1";
+  case  0: _vcregs_used += 1; return "ATTR3";
+  case  1: _vcregs_used += 1; return "ATTR4";
+  case  2: _vcregs_used += 1; return "ATTR5";
+  case  3: _vcregs_used += 1; return "ATTR6";
+  case  4: _vcregs_used += 1; return "ATTR7";
+  case  5: _vcregs_used += 1; return "ATTR1";
   }
   // These don't exist in arbvp1, though they're reportedly
   // supported by other profiles.
@@ -615,6 +621,7 @@ update_shadow_buffer(NodePath light_np) {
 //               - texmatrix
 //               - 1D/2D/3D textures, cube textures, 2D tex arrays
 //               - linear/exp/exp2 fog
+//               - animation
 //
 //               Not yet supported:
 //               - dot3_rgb and dot3_rgba combine modes
@@ -624,7 +631,7 @@ update_shadow_buffer(NodePath light_np) {
 //
 ////////////////////////////////////////////////////////////////////
 CPT(ShaderAttrib) ShaderGenerator::
-synthesize_shader(const RenderState *rs) {
+synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
   analyze_renderstate(rs);
   reset_register_allocator();
 
@@ -706,7 +713,7 @@ synthesize_shader(const RenderState *rs) {
     }
   }
   if (_vertex_colors) {
-    text << "\t in float4 vtx_color : COLOR0,\n";
+    text << "\t in float4 vtx_color : ATTR3,\n";
     text << "\t out float4 l_color : COLOR0,\n";
   }
   if (_need_world_position || _need_world_normal) {
@@ -733,7 +740,7 @@ synthesize_shader(const RenderState *rs) {
     text << "\t out float4 l_eye_normal : " << eye_normal_freg << ",\n";
   }
   if (_map_index_height >= 0 || _need_world_normal || _need_eye_normal) {
-    text << "\t in float3 vtx_normal : NORMAL,\n";
+    text << "\t in float3 vtx_normal : ATTR2,\n";
   }
   if (_map_index_height >= 0) {
     text << "\t uniform float4 mspos_view,\n";
@@ -765,11 +772,50 @@ synthesize_shader(const RenderState *rs) {
     hpos_freg = alloc_freg();
     text << "\t out float4 l_hpos : " << hpos_freg << ",\n";
   }
-  text << "\t float4 vtx_position : POSITION,\n";
+  if (anim.get_animation_type() == GeomEnums::AT_hardware &&
+      anim.get_num_transforms() > 0) {
+    int num_transforms;
+    if (anim.get_indexed_transforms()) {
+      num_transforms = 120;
+    } else {
+      num_transforms = anim.get_num_transforms();
+    }
+    text << "\t uniform float4x4 tbl_transforms[" << num_transforms << "],\n";
+    text << "\t in float4 vtx_transform_weight : ATTR1,\n";
+    if (anim.get_indexed_transforms()) {
+      text << "\t in uint4 vtx_transform_index : ATTR7,\n";
+    }
+  }
+  text << "\t in float4 vtx_position : ATTR0,\n";
   text << "\t out float4 l_position : POSITION,\n";
   text << "\t uniform float4x4 mat_modelproj\n";
   text << ") {\n";
 
+  if (anim.get_animation_type() == GeomEnums::AT_hardware &&
+      anim.get_num_transforms() > 0) {
+
+    if (!anim.get_indexed_transforms()) {
+      text << "\t const uint4 vtx_transform_index = uint4(0, 1, 2, 3);\n";
+    }
+
+    text << "\t float4x4 matrix = tbl_transforms[vtx_transform_index.x] * vtx_transform_weight.x";
+    if (anim.get_num_transforms() > 1) {
+      text << "\n\t                 + tbl_transforms[vtx_transform_index.y] * vtx_transform_weight.y";
+    }
+    if (anim.get_num_transforms() > 2) {
+      text << "\n\t                 + tbl_transforms[vtx_transform_index.z] * vtx_transform_weight.z";
+    }
+    if (anim.get_num_transforms() > 3) {
+      text << "\n\t                 + tbl_transforms[vtx_transform_index.w] * vtx_transform_weight.w";
+    }
+    text << ";\n";
+
+    text << "\t vtx_position = mul(matrix, vtx_position);\n";
+    if (_need_world_normal || _need_eye_normal) {
+      text << "\t vtx_normal = mul((float3x3)matrix, vtx_normal);\n";
+    }
+  }
+
   text << "\t l_position = mul(mat_modelproj, vtx_position);\n";
   if (_fog) {
     text << "\t l_hpos = l_position;\n";

+ 3 - 1
panda/src/pgraphnodes/shaderGenerator.h

@@ -33,6 +33,7 @@ class DirectionalLight;
 class PointLight;
 class Spotlight;
 class LightAttrib;
+class GeomVertexAnimationSpec;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ShaderGenerator
@@ -71,7 +72,8 @@ class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public TypedReferenceCount {
 PUBLISHED:
   ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host);
   virtual ~ShaderGenerator();
-  virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs);
+  virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs,
+                                              const GeomVertexAnimationSpec &anim);
 
 protected:
   CPT(RenderAttrib) create_shader_attrib(const string &txt);