瀏覽代碼

shader: Change how (light) structs are fetched

rdb 1 年之前
父節點
當前提交
3cc1793c48

+ 29 - 587
panda/src/display/graphicsStateGuardian.cxx

@@ -883,7 +883,7 @@ void GraphicsStateGuardian::
 update_shader_matrix_cache(Shader *shader, LVecBase4 *cache, int altered) {
   for (Shader::ShaderMatPart &part : shader->_mat_parts) {
     if (altered & part._dep) {
-      fetch_specified_part(part._part, part._arg, cache, part._count);
+      fetch_specified_part(part._part, part._arg, part._type, cache, part._count);
     }
     cache += part._count * part._size;
   }
@@ -1034,7 +1034,8 @@ fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache,
     break;
 
   case Shader::SMF_shader_input_ptr:
-    return fetch_ptr_parameter(spec, scratch, pad_rows);
+    return _target_shader->get_shader_input_data(spec._arg[0], scratch,
+      spec._scalar_type, spec._array_count, spec._num_rows, spec._num_cols, pad_rows);
 
   default:
     // Should never get here
@@ -1051,281 +1052,12 @@ fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache,
   }
 }
 
-/**
- * Fetches a numeric shader input, doing conversion as necessary using the
- * given amount of scratch space.
- */
-const void *GraphicsStateGuardian::
-fetch_ptr_parameter(Shader::ShaderMatSpec &spec, LVecBase4 *scratch, bool pad_rows) {
-  Shader::ShaderPtrData ptr_data;
-  if (!_target_shader->get_shader_input_ptr(spec._arg[0], ptr_data)) {
-    return nullptr;
-  }
-
-  int total_rows = std::min(spec._array_count * spec._num_rows, (int)ptr_data._size / spec._num_cols);
-  if (total_rows == 1) {
-    pad_rows = false;
-  }
-  switch (spec._scalar_type) {
-  case ShaderType::ST_float:
-    {
-      float *data = (float *)scratch;
-
-      switch (ptr_data._type) {
-      case ShaderType::ST_int:
-        // Convert int data to float data.
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = (float)(((int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const int *from_data = (const int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_uint:
-        // Convert unsigned int data to float data.
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = (float)(((unsigned int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const unsigned int *from_data = (const unsigned int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_double:
-        // Downgrade double data to float data.
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = (float)(((double *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const double *from_data = (const double *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_float:
-        if (!pad_rows || spec._num_cols == 4) {
-          // No conversion needed.
-          return (float *)ptr_data._ptr;
-        } else {
-          const float *from_data = (const float *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      default:
-#ifndef NDEBUG
-        display_cat.error()
-          << "Invalid ShaderPtrData type " << (int)ptr_data._type
-          << " for shader input '" << spec._id._name << "'\n";
-#endif
-        return nullptr;
-      }
-
-      return data;
-    }
-    break;
-
-  case ShaderType::ST_int:
-    if (ptr_data._type != ShaderType::ST_int &&
-        ptr_data._type != ShaderType::ST_uint) {
-      display_cat.error()
-        << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
-
-      // Deactivate it to make sure the user doesn't get flooded with this
-      // error.
-      spec._dep = 0;
-
-    } else {
-      return ptr_data._ptr;
-    }
-    break;
-
-  case ShaderType::ST_uint:
-    if (ptr_data._type != ShaderType::ST_uint &&
-        ptr_data._type != ShaderType::ST_int) {
-      display_cat.error()
-        << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
-
-      // Deactivate it to make sure the user doesn't get flooded with this
-      // error.
-      spec._dep = 0;
-      return nullptr;
-
-    } else {
-      return ptr_data._ptr;
-    }
-    break;
-
-  case ShaderType::ST_double:
-    {
-      double *data = (double *)scratch;
-
-      switch (ptr_data._type) {
-      case ShaderType::ST_int:
-        // Convert int data to double data.
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = (double)(((int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const int *from_data = (const int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_uint:
-        // Convert int data to double data.
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = (double)(((unsigned int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const int *from_data = (const int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_double:
-        if (!pad_rows || spec._num_cols == 4) {
-          // No conversion needed.
-          return (double *)ptr_data._ptr;
-        } else {
-          const double *from_data = (const double *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_float:
-        // Upgrade float data to double data.
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = (double)(((float *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const float *from_data = (const float *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      default:
-  #ifndef NDEBUG
-        display_cat.error()
-          << "Invalid ShaderPtrData type " << (int)ptr_data._type
-          << " for shader input '" << spec._id._name << "'\n";
-  #endif
-        return nullptr;
-      }
-
-      return data;
-    }
-    break;
-
-  case ShaderType::ST_bool:
-    {
-      unsigned int *data = (unsigned int *)scratch;
-
-      switch (ptr_data._type) {
-      case ShaderType::ST_int:
-      case ShaderType::ST_uint:
-      case ShaderType::ST_bool:
-        if (!pad_rows || spec._num_cols == 4) {
-          // No conversion needed.
-          return (unsigned int *)ptr_data._ptr;
-        } else {
-          // Pad out rows.
-          const unsigned int *from_data = (const unsigned int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (*from_data++) != 0;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_double:
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = ((double *)ptr_data._ptr)[i] != 0.0;
-          }
-        } else {
-          const double *from_data = (const double *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (*from_data++) != 0.0;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_float:
-        if (!pad_rows || spec._num_cols == 4) {
-          for (int i = 0; i < total_rows * spec._num_cols; ++i) {
-            data[i] = ((float *)ptr_data._ptr)[i] != 0.0f;
-          }
-        } else {
-          const float *from_data = (const float *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < spec._num_cols; ++c) {
-              data[i * 4 + c] = (*from_data++) != 0.0f;
-            }
-          }
-        }
-        return data;
-      }
-    }
-    break;
-
-  case ShaderType::ST_unknown:
-    break;
-  }
-
-  return nullptr;
-}
-
 /**
  * See fetch_specified_value
  */
 void GraphicsStateGuardian::
 fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
-                     LVecBase4 *into, int count) {
+                     const ShaderType *type, LVecBase4 *into, int count) {
   nassertv(count > 0);
 
   switch (part) {
@@ -1859,54 +1591,32 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
       calc_projection_mat(lens)->get_mat();
     return;
   }
-  case Shader::SMO_mat_constant_x_attrib: {
+  case Shader::SMO_struct_constant_x_light: {
+    // Could be a light, or generic struct
     if (_target_shader->has_shader_input(name)) {
-      // There is an input specifying precisely this whole thing, with dot and
-      // all.  Support this, even if only for backward compatibility.
-      _target_shader->get_shader_input_matrix(name, *(LMatrix4 *)into);
+      const NodePath &np = _target_shader->get_shader_input_nodepath(name);
+      fetch_specified_light(np, into);
       return;
     }
-
-    const NodePath &np = _target_shader->get_shader_input_nodepath(name->get_parent());
-    const PandaNode *node = np.node();
-
-    // This is the only matrix member we support from NodePath inputs.
-    if (node != nullptr && node->is_of_type(LensNode::get_class_type()) &&
-        name->get_basename() == "shadowViewMatrix") {
-      const LensNode *lnode = (const LensNode *)node;
-      const Lens *lens = lnode->get_lens();
-
-      LMatrix4 t = _inv_cs_transform->get_mat() *
-        _scene_setup->get_camera_transform()->get_mat() *
-        np.get_net_transform()->get_inverse()->get_mat() *
-        LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system());
-
-      if (!lnode->is_of_type(PointLight::get_class_type())) {
-        t *= lens->get_projection_mat() * shadow_bias_mat;
+    // fall through
+  }
+  case Shader::SMO_struct_constant_x: {
+    int offset = 0;
+    const ShaderType::Struct *struct_type = type->as_struct();
+    for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
+      const ShaderType::Struct::Member &member = struct_type->get_member(i);
+
+      ShaderType::ScalarType scalar_type;
+      uint32_t num_elements;
+      uint32_t num_rows;
+      uint32_t num_columns;
+      if (member.type->as_scalar_type(scalar_type, num_elements, num_rows, num_columns)) {
+        Shader::ShaderPtrData data;
+        _target_shader->get_shader_input_data(((InternalName *)name)->append(member.name), &into[offset], scalar_type, num_elements, num_rows, num_columns, true);
+        offset += num_elements * num_rows;
       }
-      *(LMatrix4 *)into = t;
-    }
-    else {
-      display_cat.error()
-        << "Shader input " << *name << " requests invalid attribute "
-        << name->get_basename() << " from node " << np << "\n";
-      *(LMatrix4 *)into = LMatrix4::ident_mat();
     }
-    return;
-  }
-  case Shader::SMO_vec_constant_x_attrib: {
-    if (_target_shader->has_shader_input(name)) {
-      // There is an input specifying precisely this whole thing, with dot and
-      // all.  Support this, even if only for backward compatibility.
-      into[0] = _target_shader->get_shader_input_vector(name);
-      return;
-    }
-
-    const NodePath &np = _target_shader->get_shader_input_nodepath(name->get_parent());
-    nassertv(!np.is_empty());
-
-    fetch_specified_member(np, name->get_basename(), into[0]);
-    return;
+    break;
   }
   case Shader::SMO_light_source_i: {
     const LightAttrib *target_light;
@@ -2073,261 +1783,6 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
   }
 }
 
-/**
- * Given a NodePath passed into a shader input that is a structure, fetches
- * the value for the given member.
- */
-void GraphicsStateGuardian::
-fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4 &v) {
-  // This system is not ideal.  It will be improved in the future.
-  static const CPT_InternalName IN_color("color");
-  static const CPT_InternalName IN_ambient("ambient");
-  static const CPT_InternalName IN_diffuse("diffuse");
-  static const CPT_InternalName IN_specular("specular");
-  static const CPT_InternalName IN_position("position");
-  static const CPT_InternalName IN_halfVector("halfVector");
-  static const CPT_InternalName IN_spotDirection("spotDirection");
-  static const CPT_InternalName IN_spotCutoff("spotCutoff");
-  static const CPT_InternalName IN_spotCosCutoff("spotCosCutoff");
-  static const CPT_InternalName IN_spotExponent("spotExponent");
-  static const CPT_InternalName IN_attenuation("attenuation");
-  static const CPT_InternalName IN_constantAttenuation("constantAttenuation");
-  static const CPT_InternalName IN_linearAttenuation("linearAttenuation");
-  static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation");
-
-  PandaNode *node = nullptr;
-  if (!np.is_empty()) {
-    node = np.node();
-  }
-
-  if (attrib == IN_color) {
-    if (node == nullptr) {
-      v.set(0, 0, 0, 1);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-    v = light->get_color();
-  }
-  else if (attrib == IN_ambient) {
-    if (node == nullptr) {
-      v.set(0, 0, 0, 1);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-    if (node->is_ambient_light()) {
-      v = light->get_color();
-    } else {
-      // Non-ambient lights don't currently have an ambient color in Panda3D.
-      v.set(0, 0, 0, 1);
-    }
-  }
-  else if (attrib == IN_diffuse) {
-    if (node == nullptr) {
-      v.set(0, 0, 0, 1);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-    if (node->is_ambient_light()) {
-      // Ambient light has no diffuse color.
-      v.set(0, 0, 0, 1);
-    } else {
-      v = light->get_color();
-    }
-  }
-  else if (attrib == IN_specular) {
-    if (node == nullptr) {
-      v.set(0, 0, 0, 1);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-    v = light->get_specular_color();
-  }
-  else if (attrib == IN_position) {
-    if (np.is_empty()) {
-      v.set(0, 0, 1, 0);
-    }
-    else if (node->is_ambient_light()) {
-      // Ambient light has no position.
-      v.set(0, 0, 0, 0);
-    }
-    else if (node->is_of_type(DirectionalLight::get_class_type())) {
-      DirectionalLight *light;
-      DCAST_INTO_V(light, node);
-
-      CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
-      LVector3 dir = -(light->get_direction() * transform->get_mat());
-      dir *= _scene_setup->get_cs_world_transform()->get_mat();
-      v.set(dir[0], dir[1], dir[2], 0);
-    }
-    else {
-      LightLensNode *light;
-      DCAST_INTO_V(light, node);
-      Lens *lens = light->get_lens();
-      nassertv(lens != nullptr);
-
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
-          np.get_transform(_scene_setup->get_scene_root().get_parent()));
-
-      const LMatrix4 &light_mat = transform->get_mat();
-      LPoint3 pos = lens->get_nodal_point() * light_mat;
-      v.set(pos[0], pos[1], pos[2], 1);
-    }
-  }
-  else if (attrib == IN_halfVector) {
-    if (np.is_empty()) {
-      v.set(0, 0, 1, 0);
-    }
-    else if (node->is_ambient_light()) {
-      // Ambient light has no half-vector.
-      v.set(0, 0, 0, 0);
-    }
-    else if (node->is_of_type(DirectionalLight::get_class_type())) {
-      DirectionalLight *light;
-      DCAST_INTO_V(light, node);
-
-      CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
-      LVector3 dir = -(light->get_direction() * transform->get_mat());
-      dir *= _scene_setup->get_cs_world_transform()->get_mat();
-      dir.normalize();
-      dir += LVector3(0, 0, 1);
-      dir.normalize();
-      v.set(dir[0], dir[1], dir[2], 1);
-    }
-    else {
-      LightLensNode *light;
-      DCAST_INTO_V(light, node);
-      Lens *lens = light->get_lens();
-      nassertv(lens != nullptr);
-
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
-          np.get_transform(_scene_setup->get_scene_root().get_parent()));
-
-      const LMatrix4 &light_mat = transform->get_mat();
-      LPoint3 pos = lens->get_nodal_point() * light_mat;
-      pos.normalize();
-      pos += LVector3(0, 0, 1);
-      pos.normalize();
-      v.set(pos[0], pos[1], pos[2], 1);
-    }
-  }
-  else if (attrib == IN_spotDirection) {
-    if (node == nullptr) {
-      v.set(0, 0, -1, 0);
-    }
-    else if (node->is_ambient_light()) {
-      // Ambient light has no spot direction.
-      v.set(0, 0, 0, 0);
-    }
-    else {
-      LightLensNode *light;
-      DCAST_INTO_V(light, node);
-      Lens *lens = light->get_lens();
-      nassertv(lens != nullptr);
-
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
-          np.get_transform(_scene_setup->get_scene_root().get_parent()));
-
-      const LMatrix4 &light_mat = transform->get_mat();
-      LVector3 dir = lens->get_view_vector() * light_mat;
-      v.set(dir[0], dir[1], dir[2], 0);
-    }
-  }
-  else if (attrib == IN_spotCutoff) {
-    if (node != nullptr &&
-        node->is_of_type(Spotlight::get_class_type())) {
-      LightLensNode *light;
-      DCAST_INTO_V(light, node);
-      Lens *lens = light->get_lens();
-      nassertv(lens != nullptr);
-
-      float cutoff = lens->get_hfov() * 0.5f;
-      v.fill(cutoff);
-    }
-    else {
-      // Other lights have no cut-off.
-      v.fill(180);
-    }
-  }
-  else if (attrib == IN_spotCosCutoff) {
-    if (node != nullptr &&
-        node->is_of_type(Spotlight::get_class_type())) {
-      LightLensNode *light;
-      DCAST_INTO_V(light, node);
-      Lens *lens = light->get_lens();
-      nassertv(lens != nullptr);
-
-      float cutoff = lens->get_hfov() * 0.5f;
-      v.fill(ccos(deg_2_rad(cutoff)));
-    } else {
-      // Other lights have no cut-off.
-      v.fill(-1);
-    }
-  }
-  else if (attrib == IN_spotExponent) {
-    if (node == nullptr) {
-      v.fill(0);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-
-    v.fill(light->get_exponent());
-  }
-  else if (attrib == IN_attenuation) {
-    if (node != nullptr) {
-      Light *light = node->as_light();
-      nassertv(light != nullptr);
-
-      v = LVecBase4(light->get_attenuation(), 0);
-    } else {
-      v.set(1, 0, 0, 0);
-    }
-  }
-  else if (attrib == IN_constantAttenuation) {
-    if (node == nullptr) {
-      v.fill(1);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-
-    v.fill(light->get_attenuation()[0]);
-  }
-  else if (attrib == IN_linearAttenuation) {
-    if (node == nullptr) {
-      v.fill(0);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-
-    v.fill(light->get_attenuation()[1]);
-  }
-  else if (attrib == IN_quadraticAttenuation) {
-    if (node == nullptr) {
-      v.fill(0);
-      return;
-    }
-    Light *light = node->as_light();
-    nassertv(light != nullptr);
-
-    v.fill(light->get_attenuation()[2]);
-  }
-  else {
-    display_cat.error()
-      << "Shader input requests invalid attribute " << *attrib
-      << " from node " << np << "\n";
-    v.set(0, 0, 0, 1);
-  }
-}
-
 /**
  * Given a NodePath passed into a shader input that is a structure, fetches
  * the value for the given member.
@@ -2421,7 +1876,10 @@ fetch_specified_light(const NodePath &np, LVecBase4 *into) {
       if (!node->is_of_type(PointLight::get_class_type())) {
         t *= lens->get_projection_mat() * shadow_bias_mat;
       }
-      *(LMatrix4 *)&into[Shader::LA_shadow_view_matrix] = t;
+      into[Shader::LA_shadow_view_matrix + 0] = t[0];
+      into[Shader::LA_shadow_view_matrix + 1] = t[1];
+      into[Shader::LA_shadow_view_matrix + 2] = t[2];
+      into[Shader::LA_shadow_view_matrix + 3] = t[3];
     }
 
     LVecBase3 atten = light->get_attenuation();
@@ -2733,22 +2191,6 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
   return nullptr;
 }
 
-/**
- * Return a pointer to struct ShaderPtrData
- */
-const Shader::ShaderPtrData *GraphicsStateGuardian::
-fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec) {
-  return (_target_shader->get_shader_input_ptr(spec._arg));
-}
-
-/**
- *
- */
-bool GraphicsStateGuardian::
-fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec, Shader::ShaderPtrData &data) {
-  return _target_shader->get_shader_input_ptr(spec._arg, data);
-}
-
 /**
  * Makes the specified DisplayRegion current.  All future drawing and clear
  * operations will be constrained within the given DisplayRegion.

+ 1 - 6
panda/src/display/graphicsStateGuardian.h

@@ -341,16 +341,11 @@ public:
   void update_shader_matrix_cache(Shader *shader, LVecBase4 *cache, int altered);
   const void *fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache,
                                     LVecBase4 *scratch, bool pad_rows);
-  const void *fetch_ptr_parameter(Shader::ShaderMatSpec &spec, LVecBase4 *scratch, bool pad_rows);
   void fetch_specified_part(Shader::ShaderMatInput input, const InternalName *name,
-                            LVecBase4 *into, int count = 1);
-  void fetch_specified_member(const NodePath &np, CPT_InternalName member,
-                              LVecBase4 &v);
+                            const ShaderType *type, LVecBase4 *into, int count = 1);
   void fetch_specified_light(const NodePath &np, LVecBase4 *into);
   PT(Texture) fetch_specified_texture(Shader::ShaderTexSpec &spec,
                                       SamplerState &sampler, int &view);
-  const Shader::ShaderPtrData *fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec);
-  bool fetch_ptr_parameter(const Shader::ShaderPtrSpec &spec, Shader::ShaderPtrData &data);
 
   virtual void prepare_display_region(DisplayRegionPipelineReader *dr);
   virtual void clear_before_callback();

+ 5 - 11
panda/src/glstuff/glShaderContext_src.cxx

@@ -1461,6 +1461,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
         bind._id = param;
         bind._piece = Shader::SMP_mat4_whole;
         bind._func = Shader::SMF_first;
+        bind._part[0] = Shader::SMO_mat_constant_x;
+        bind._arg[0] = param._name;
         bind._part[1] = Shader::SMO_identity;
         bind._arg[1] = nullptr;
         if (param._name->get_basename() == "shadowMatrix") {
@@ -1470,19 +1472,11 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           static bool warned = false;
           if (!warned) {
             warned = true;
-            GLCAT.warning()
-              << "light.shadowMatrix inputs are deprecated; use "
+            GLCAT.error()
+              << "light.shadowMatrix inputs are no longer supported; use "
                  "shadowViewMatrix instead, which transforms from view "
                  "space instead of model space.\n";
           }
-          bind._func = Shader::SMF_compose;
-          bind._part[0] = Shader::SMO_model_to_apiview;
-          bind._arg[0] = nullptr;
-          bind._part[1] = Shader::SMO_mat_constant_x_attrib;
-          bind._arg[1] = param._name->get_parent()->append("shadowViewMatrix");
-        } else {
-          bind._part[0] = Shader::SMO_mat_constant_x_attrib;
-          bind._arg[0] = param._name;
         }
         _shader->cp_add_mat_spec(bind);
       } else {
@@ -1514,7 +1508,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           bind._piece = Shader::SMP_vec4;
         }
         bind._func = Shader::SMF_first;
-        bind._part[0] = Shader::SMO_vec_constant_x_attrib;
+        bind._part[0] = Shader::SMO_vec_constant_x;
         bind._arg[0] = param._name;
         bind._part[1] = Shader::SMO_identity;
         bind._arg[1] = nullptr;

+ 236 - 183
panda/src/gobj/shader.cxx

@@ -252,6 +252,106 @@ expect_coordinate_system(const InternalName *name, const ::ShaderType *type,
   return true;
 }
 
+/**
+ *
+ */
+bool Shader::
+check_light_struct_member(const string &name, const ShaderType *type,
+                          ShaderMatPiece &piece, int &offset) {
+
+  ShaderMatPiece expected = SMP_vec4;
+  if (name == "color") {
+    offset = 4 * Shader::LA_color;
+  }
+  else if (name == "specular") {
+    offset = 4 * Shader::LA_specular;
+  }
+  else if (name == "ambient") {
+    offset = 4 * Shader::LA_ambient;
+  }
+  else if (name == "diffuse") {
+    offset = 4 * Shader::LA_diffuse;
+  }
+  else if (name == "position") {
+    offset = 4 * Shader::LA_position;
+  }
+  else if (name == "halfVector") {
+    offset = 4 * Shader::LA_half_vector;
+  }
+  else if (name == "spotDirection") {
+    offset = 4 * Shader::LA_spot_direction;
+  }
+  else if (name == "spotCosCutoff") {
+    offset = 4 * Shader::LA_spot_params;
+    expected = SMP_scalar;
+  }
+  else if (name == "spotCutoff") {
+    offset = 4 * Shader::LA_spot_params + 1;
+    expected = SMP_scalar;
+  }
+  else if (name == "spotExponent") {
+    offset = 4 * Shader::LA_spot_params + 2;
+    expected = SMP_scalar;
+  }
+  else if (name == "attenuation") {
+    offset = 4 * Shader::LA_attenuation;
+    expected = SMP_vec3;
+  }
+  else if (name == "constantAttenuation") {
+    offset = 4 * Shader::LA_attenuation;
+    expected = SMP_scalar;
+  }
+  else if (name == "linearAttenuation") {
+    offset = 4 * Shader::LA_attenuation + 1;
+    expected = SMP_scalar;
+  }
+  else if (name == "quadraticAttenuation") {
+    offset = 4 * Shader::LA_attenuation + 2;
+    expected = SMP_scalar;
+  }
+  else if (name == "radius") {
+    offset = 4 * Shader::LA_attenuation + 3;
+    expected = SMP_scalar;
+  }
+  else if (name == "shadowViewMatrix") {
+    offset = 4 * Shader::LA_shadow_view_matrix;
+    expected = SMP_mat4_whole;
+  }
+  else {
+    return false;
+  }
+
+  const ::ShaderType::Matrix *matrix = type->as_matrix();
+  if (matrix != nullptr) {
+    if (matrix->get_num_rows() != 4 || matrix->get_num_columns() != 4) {
+      return false;
+    }
+    piece = SMP_mat4_whole;
+  }
+  else {
+    const ::ShaderType::Vector *vector = type->as_vector();
+    if (vector == nullptr || vector->get_num_components() == 1) {
+      piece = SMP_scalar;
+    }
+    else if (vector->get_num_components() == 2) {
+      piece = SMP_vec2;
+    }
+    else if (vector->get_num_components() == 3) {
+      piece = SMP_vec3;
+    }
+    else {
+      piece = SMP_vec4;
+    }
+  }
+
+  // It's okay to declare as vec3 if we allow vec4.
+  if (piece != expected && (expected != SMP_vec4 || piece != SMP_vec3)) {
+    return false;
+  }
+
+  return true;
+}
+
 /**
  * Given ShaderMatInput, returns an indication of what part or parts of the
  * state_and_transform the ShaderMatInput depends upon.
@@ -305,9 +405,9 @@ cp_dependency(ShaderMatInput inp) {
       (inp == SMO_slight_x) ||
       (inp == SMO_satten_x) ||
       (inp == SMO_mat_constant_x) ||
-      (inp == SMO_mat_constant_x_attrib) ||
       (inp == SMO_vec_constant_x) ||
-      (inp == SMO_vec_constant_x_attrib) ||
+      (inp == SMO_struct_constant_x) ||
+      (inp == SMO_struct_constant_x_light) ||
       (inp == SMO_view_x_to_view) ||
       (inp == SMO_view_to_view_x) ||
       (inp == SMO_apiview_x_to_view) ||
@@ -326,8 +426,8 @@ cp_dependency(ShaderMatInput inp) {
         (inp == SMO_slight_x) ||
         (inp == SMO_satten_x) ||
         (inp == SMO_mat_constant_x) ||
-        (inp == SMO_mat_constant_x_attrib) ||
-        (inp == SMO_vec_constant_x_attrib) ||
+        (inp == SMO_struct_constant_x) ||
+        (inp == SMO_struct_constant_x_light) ||
         (inp == SMO_view_x_to_view) ||
         (inp == SMO_view_to_view_x) ||
         (inp == SMO_apiview_x_to_view) ||
@@ -350,8 +450,8 @@ cp_dependency(ShaderMatInput inp) {
   if (inp == SMO_light_source_i ||
       inp == SMO_apiview_to_apiclip_light_source_i ||
       inp == SMO_light_source_i_packed ||
-      inp == SMO_mat_constant_x_attrib ||
-      inp == SMO_vec_constant_x_attrib) {
+      inp == SMO_struct_constant_x ||
+      inp == SMO_struct_constant_x_light) {
     // Some light attribs (eg. position) need to be transformed to view space.
     dep |= SSD_view_transform;
   }
@@ -399,7 +499,7 @@ cp_dependency(ShaderMatInput inp) {
  * Given ShaderMatInput, returns the size in the cache that this part requires.
  */
 int Shader::
-cp_size(ShaderMatInput inp) {
+cp_size(ShaderMatInput inp, const ::ShaderType *type) {
   switch (inp) {
   case SMO_INVALID:
     return 0;
@@ -417,7 +517,6 @@ cp_size(ShaderMatInput inp) {
   case SMO_frame_number:
   case SMO_frame_time:
   case SMO_frame_delta:
-  case SMO_vec_constant_x_attrib:
   case SMO_light_ambient:
   case SMO_light_product_i_ambient:
   case SMO_light_product_i_diffuse:
@@ -455,7 +554,6 @@ cp_size(ShaderMatInput inp) {
   case SMO_view_to_clip_x:
   case SMO_apiclip_x_to_view:
   case SMO_view_to_apiclip_x:
-  case SMO_mat_constant_x_attrib:
   case SMO_apiview_to_apiclip_light_source_i:
   case SMO_model_to_apiview:
   case SMO_apiview_to_model:
@@ -469,10 +567,14 @@ cp_size(ShaderMatInput inp) {
     return MA_COUNT;
 
   case SMO_light_source_i:
+  case SMO_struct_constant_x_light:
     return LA_COUNT;
 
   case SMO_attr_fog:
     return FA_COUNT;
+
+  case SMO_struct_constant_x:
+    return type->get_num_interface_locations();
   }
 
   nassertr(false, 0);
@@ -648,13 +750,14 @@ cp_add_mat_spec(ShaderMatSpec &spec) {
       }
       offset += part._count * part._size;
     }
-    int size = cp_size(spec._part[p]);
+    int size = cp_size(spec._part[p], spec._id._type);
     if (i == _mat_parts.size()) {
       // Didn't find this part yet, create a new one.
       ShaderMatPart part;
       part._part = spec._part[p];
-      part._count = end[p];
       part._arg = spec._arg[p];
+      part._type = spec._id._type;
+      part._count = end[p];
       part._dep = dep;
       part._size = size;
 
@@ -1852,17 +1955,12 @@ bind_parameter(const Parameter &param) {
               return false;
             }
             bind._piece = SMP_mat4_whole;
-            bind._part[0] = SMO_apiview_to_apiclip_light_source_i;
+            bind._part[0] = SMO_light_source_i;
             bind._arg[0] = nullptr;
             bind._part[1] = SMO_identity;
             bind._arg[1] = nullptr;
             bind._scalar_type = member.type->as_matrix()->get_scalar_type();
-          }
-          else if (member.name == "shadowViewMatrixInverse") {
-            shader_cat.error()
-              << "p3d_LightSource struct does not provide a matrix named "
-              << "shadowViewMatrixInverse!\n";
-            return false;
+            bind._offset = 4 * LA_shadow_view_matrix;
           }
           else if (member.name == "shadowMatrix") {
             // Only supported for backward compatibility: includes the model
@@ -1892,90 +1990,16 @@ bind_parameter(const Parameter &param) {
             if (!expect_float_vector(fqname, member.type, 1, 4)) {
               return false;
             }
-            const ::ShaderType::Vector *vector = member.type->as_vector();
-            if (vector == nullptr || vector->get_num_components() == 1) {
-              bind._piece = SMP_scalar;
-            }
-            else if (vector->get_num_components() == 2) {
-              bind._piece = SMP_vec2;
-            }
-            else if (vector->get_num_components() == 3) {
-              bind._piece = SMP_vec3;
-            }
-            else {
-              bind._piece = SMP_vec4;
-            }
             bind._part[0] = SMO_light_source_i;
             bind._arg[0] = nullptr;
             bind._part[1] = SMO_identity;
             bind._arg[1] = nullptr;
             bind._scalar_type = ScalarType::ST_float;
 
-            ShaderMatPiece expected = SMP_vec4;
-            if (member.name == "color") {
-              bind._offset = 4 * Shader::LA_color;
-            }
-            else if (member.name == "specular") {
-              bind._offset = 4 * Shader::LA_specular;
-            }
-            else if (member.name == "ambient") {
-              bind._offset = 4 * Shader::LA_ambient;
-            }
-            else if (member.name == "diffuse") {
-              bind._offset = 4 * Shader::LA_diffuse;
-            }
-            else if (member.name == "position") {
-              bind._offset = 4 * Shader::LA_position;
-            }
-            else if (member.name == "halfVector") {
-              bind._offset = 4 * Shader::LA_half_vector;
-            }
-            else if (member.name == "spotDirection") {
-              bind._offset = 4 * Shader::LA_spot_direction;
-            }
-            else if (member.name == "spotCosCutoff") {
-              bind._offset = 4 * Shader::LA_spot_params;
-              expected = SMP_scalar;
-            }
-            else if (member.name == "spotCutoff") {
-              bind._offset = 4 * Shader::LA_spot_params + 1;
-              expected = SMP_scalar;
-            }
-            else if (member.name == "spotExponent") {
-              bind._offset = 4 * Shader::LA_spot_params + 2;
-              expected = SMP_scalar;
-            }
-            else if (member.name == "attenuation") {
-              bind._offset = 4 * Shader::LA_attenuation;
-              expected = SMP_vec3;
-            }
-            else if (member.name == "constantAttenuation") {
-              bind._offset = 4 * Shader::LA_attenuation;
-              expected = SMP_scalar;
-            }
-            else if (member.name == "linearAttenuation") {
-              bind._offset = 4 * Shader::LA_attenuation + 1;
-              expected = SMP_scalar;
-            }
-            else if (member.name == "quadraticAttenuation") {
-              bind._offset = 4 * Shader::LA_attenuation + 2;
-              expected = SMP_scalar;
-            }
-            else if (member.name == "radius") {
-              bind._offset = 4 * Shader::LA_attenuation + 3;
-              expected = SMP_scalar;
-            }
-            else {
-              shader_cat.error()
-                << "Invalid light struct member " << member.name << "\n";
-              return false;
-            }
-
-            // It's okay to declare as vec3 if we allow vec4.
-            if (bind._piece != expected && (expected != SMP_vec4 || bind._piece != SMP_vec3)) {
+            if (!check_light_struct_member(member.name, member.type, bind._piece, bind._offset)) {
               shader_cat.error()
-                << "p3d_LightSource[]." << member.name << " has unexpected type "
-                << bind._piece << ", expected " << expected << "\n";
+                << "Invalid light struct member "
+                << *member.type << " " << member.name << "\n";
               return false;
             }
           }
@@ -2846,95 +2870,9 @@ bind_parameter(const Parameter &param) {
   }
   else if (const ::ShaderType::Struct *struct_type = type->as_struct()) {
     // Is this a struct?  If so, bind the individual members.
-    bool success = true;
-
     int location = param._location;
-
-    for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
-      const ::ShaderType::Struct::Member &member = struct_type->get_member(i);
-
-      PT(InternalName) fqname = ((InternalName *)name.p())->append(member.name);
-
-      // Numeric struct members under GLSL may need a special treatment.
-      ScalarType scalar_type;
-      uint32_t dim[3];
-      if (_language == SL_GLSL &&
-          member.type->as_scalar_type(scalar_type, dim[0], dim[1], dim[2]) &&
-          (scalar_type == ScalarType::ST_float || scalar_type == ScalarType::ST_double) &&
-          dim[0] == 1) {
-        // It might be something like an attribute of a shader input, like a
-        // light parameter.  It might also just be a custom struct parameter.
-        // We can't know yet, so we always have to handle it specially.
-        ShaderMatSpec bind;
-        bind._id = param;
-        bind._id._name = fqname;
-        bind._id._type = member.type;
-        bind._id._location = location;
-        bind._scalar_type = scalar_type;
-        if (member.name == "shadowMatrix" && dim[1] == 4 && dim[2] == 4) {
-          // Special exception for shadowMatrix, which is deprecated because it
-          // includes the model transformation.  It is far more efficient to do
-          // that in the shader instead.
-          static bool warned = false;
-          if (!warned) {
-            warned = true;
-            shader_cat.warning()
-              << "light.shadowMatrix inputs are deprecated; use "
-                 "shadowViewMatrix instead, which transforms from view "
-                 "space instead of model space.\n";
-          }
-          bind._piece = SMP_mat4_whole;
-          bind._func = SMF_compose;
-          bind._part[0] = SMO_model_to_apiview;
-          bind._arg[0] = nullptr;
-          bind._part[1] = SMO_mat_constant_x_attrib;
-          bind._arg[1] = ((InternalName *)name.p())->append("shadowViewMatrix");
-        }
-        else {
-          bind._func = SMF_first;
-          if (dim[1] == 4) {
-            bind._piece = SMP_mat4_whole;
-            bind._part[0] = SMO_mat_constant_x_attrib;
-          }
-          else if (dim[1] == 3) {
-            bind._piece = SMP_mat4_upper3x3;
-            bind._part[0] = SMO_mat_constant_x_attrib;
-          }
-          else {
-            bind._part[0] = SMO_vec_constant_x_attrib;
-            if (dim[2] == 1) {
-              bind._piece = SMP_scalar;
-            }
-            else if (dim[2] == 2) {
-              bind._piece = SMP_vec2;
-            }
-            else if (dim[2] == 3) {
-              bind._piece = SMP_vec3;
-            }
-            else {
-              bind._piece = SMP_vec4;
-            }
-          }
-          bind._arg[0] = fqname;
-          bind._part[1] = SMO_identity;
-          bind._arg[1] = nullptr;
-        }
-        cp_add_mat_spec(bind);
-      }
-      else {
-        // Otherwise, recurse.
-        Parameter member_param(param);
-        member_param._name = fqname;
-        member_param._type = member.type;
-        member_param._location = location;
-        if (!bind_parameter(member_param)) {
-          success = false;
-        }
-      }
-      location += member.type->get_num_parameter_locations();
-    }
-
-    return success;
+    int offset = 0;
+    return r_bind_struct_members(param, name, struct_type, location, offset);
   }
   else if (const ::ShaderType::Array *array_type = type->as_array()) {
     // Check if this is an array of structs.
@@ -3022,6 +2960,121 @@ bind_parameter(const Parameter &param) {
   return false;
 }
 
+/**
+ * Recursive version of the above function to bind struct members.
+ */
+bool Shader::
+r_bind_struct_members(const Parameter &param, const InternalName *name,
+                      const ::ShaderType::Struct *struct_type,
+                      int &location, int &offset) {
+
+  bool success = true;
+
+  // Check if this could be a light structure.
+  bool maybe_light_struct = false;
+  if (_language == SL_GLSL && struct_type->get_num_members() > 0) {
+    maybe_light_struct = true;
+    for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
+      const ::ShaderType::Struct::Member &member = struct_type->get_member(i);
+      ShaderMatPiece piece;
+      int offset;
+      if (!check_light_struct_member(member.name, member.type, piece, offset)) {
+        if (member.name != "shadowMatrix") {
+          maybe_light_struct = false;
+          break;
+        }
+      }
+    }
+  }
+
+  for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
+    const ::ShaderType::Struct::Member &member = struct_type->get_member(i);
+
+    PT(InternalName) fqname = ((InternalName *)name)->append(member.name);
+
+    // Members under a GLSL light struct may need a special treatment.
+    ScalarType scalar_type;
+    uint32_t dim[3];
+    if (maybe_light_struct &&
+        member.type->as_scalar_type(scalar_type, dim[0], dim[1], dim[2])) {
+      // It might be something like an attribute of a shader input, like a
+      // light parameter.  It might also just be a custom struct parameter.
+      // We can't know yet, so we always have to handle it specially.
+      ShaderMatSpec bind;
+      bind._id = param;
+      bind._id._name = fqname;
+      bind._id._type = member.type;
+      bind._id._location = location;
+      bind._scalar_type = scalar_type;
+      if (member.name == "shadowMatrix" &&
+          dim[0] == 1 && dim[1] == 4 && dim[2] == 4) {
+        // This has been deprecated for a while and is no longer supported.
+        static bool warned = false;
+        if (!warned) {
+          warned = true;
+          shader_cat.error()
+            << "light.shadowMatrix inputs are no longer supported; use "
+               "shadowViewMatrix instead, which transforms from view "
+               "space instead of model space.\n";
+        }
+      }
+      bind._func = SMF_first;
+      bind._id._type = struct_type;
+      bind._part[0] = SMO_struct_constant_x;
+      bind._arg[0] = name;
+      bind._part[1] = SMO_identity;
+      bind._arg[1] = nullptr;
+      bind._offset = offset;
+
+      if (maybe_light_struct) {
+        bind._part[0] = SMO_struct_constant_x_light;
+        check_light_struct_member(member.name, member.type, bind._piece, bind._offset);
+      }
+      else if (dim[1] == 4) {
+        bind._piece = SMP_mat4_whole;
+      }
+      else if (dim[1] == 3) {
+        bind._piece = SMP_mat4_upper3x3;
+      }
+      else if (dim[2] == 1) {
+        bind._piece = SMP_scalar;
+      }
+      else if (dim[2] == 2) {
+        bind._piece = SMP_vec2;
+      }
+      else if (dim[2] == 3) {
+        bind._piece = SMP_vec3;
+      }
+      else {
+        bind._piece = SMP_vec4;
+      }
+      cp_add_mat_spec(bind);
+
+      offset += dim[0] * dim[1] * 4;
+      location += dim[0];
+    }
+    /*else if (const ::ShaderType::Struct *nested = member.type->as_struct()) {
+      // Recurse.
+      if (!r_bind_struct_members(param, fqname, nested, location, offset)) {
+        success = false;
+      }
+    }*/
+    else {
+      // If it's any other type, bind as dotted parameter.
+      Parameter member_param(param);
+      member_param._name = fqname;
+      member_param._type = member.type;
+      member_param._location = location;
+      if (!bind_parameter(member_param)) {
+        success = false;
+      }
+      location += member.type->get_num_parameter_locations();
+    }
+  }
+
+  return success;
+}
+
 /**
  * Checks whether the shader or any of its dependent files were modified on
  * disk.

+ 9 - 3
panda/src/gobj/shader.h

@@ -192,8 +192,8 @@ public:
     SMO_frame_time,
     SMO_frame_delta,
 
-    SMO_mat_constant_x_attrib,
-    SMO_vec_constant_x_attrib,
+    SMO_struct_constant_x,
+    SMO_struct_constant_x_light, // looks like light struct, may not be!
 
     SMO_light_ambient,
     SMO_light_source_i,
@@ -414,6 +414,7 @@ public:
   struct ShaderMatPart {
     ShaderMatInput _part;
     CPT(InternalName) _arg;
+    const ShaderType *_type;
     int _size = 1;
     int _count = 1;
     int _dep = SSD_NONE;
@@ -514,8 +515,10 @@ protected:
   bool expect_coordinate_system(const InternalName *name, const ::ShaderType *type,
                                 vector_string &pieces, int &next,
                                 ShaderMatSpec &spec, bool fromflag);
+  static bool check_light_struct_member(const std::string &name, const ::ShaderType *type,
+                                        ShaderMatPiece &piece, int &offset);
   int cp_dependency(ShaderMatInput inp);
-  int cp_size(ShaderMatInput inp);
+  int cp_size(ShaderMatInput inp, const ::ShaderType *type);
 
 public:
   void cp_add_mat_spec(ShaderMatSpec &spec);
@@ -603,6 +606,9 @@ public:
   bool link();
   bool bind_vertex_input(const InternalName *name, const ::ShaderType *type, int location);
   bool bind_parameter(const Parameter &parameter);
+  bool r_bind_struct_members(const Parameter &param, const InternalName *name,
+                             const ::ShaderType::Struct *struct_type,
+                             int &location, int &offset);
 
   bool check_modified() const;
   ShaderCompiler *get_compiler(ShaderLanguage lang) const;

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

@@ -468,7 +468,8 @@ get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const
       }
     }
     ostringstream strm;
-    strm << "Shader input " << id->get_name() << " was given an incompatible parameter type.\n";
+    strm << "Shader input " << id->get_name() << " was given an incompatible parameter type ("
+         << p.get_value_type() << ").\n";
     nassert_raise(strm.str());
     return false;
   } else {
@@ -479,6 +480,339 @@ get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const
   }
 }
 
+/**
+ * Extracts the shader input data according to the given type expected by the
+ * shader.  Returns the number of bytes written to "into".
+ */
+size_t ShaderAttrib::
+get_shader_input_data(const InternalName *id, void *into,
+                      const ShaderType *type, bool pad_rows) const {
+  ShaderType::ScalarType scalar_type;
+  uint32_t num_elements;
+  uint32_t num_rows;
+  uint32_t num_columns;
+  if (type->as_scalar_type(scalar_type, num_elements, num_rows, num_columns)) {
+    Shader::ShaderPtrData data;
+    get_shader_input_data(id, into, scalar_type, num_elements, num_rows, num_columns, pad_rows, true);
+    return num_elements * num_rows * (pad_rows ? 16 : num_columns * 4);
+  }
+  /*else if (const ShaderType::Array *array_type = type->as_array()) {
+    size_t total_size = 0;
+    for (size_t i = 0; i < array_type->get_num_elements(); ++i) {
+      total_size += get_shader_input_data(id, into, type, rows);
+    }
+  }*/
+  /*else if (const ShaderType::Struct *struct_type = type->as_struct()) {
+    size_t total_size = 0;
+    for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
+      const ShaderType::Struct::Member &member = struct_type->get_member(i);
+
+      size_t size = get_shader_input_data(((InternalName *)id)->append(member.name), into, member.type, pad_rows);
+      total_size += size;
+      into = (char *)into + size;
+    }
+    return total_size;
+  }*/
+  else {
+    return 0;
+  }
+}
+
+/**
+ * Extracts the shader input data, converting it as necessary.  The scratch
+ * pointer must be large enough to contain the data, but may or may not be
+ * filled by this function (depending on whether conversion is needed), unless
+ * always_copy is true.
+ */
+void *ShaderAttrib::
+get_shader_input_data(const InternalName *id, void *scratch,
+                      ShaderType::ScalarType scalar_type, int num_elements,
+                      int num_rows, int num_columns, bool pad_rows,
+                      bool always_copy) const {
+  Shader::ShaderPtrData ptr_data;
+  if (!get_shader_input_ptr(id, ptr_data)) {
+    return nullptr;
+  }
+
+  int total_rows = std::min(num_elements * num_rows, (int)ptr_data._size / num_columns);
+  if (total_rows == 1) {
+    pad_rows = false;
+  }
+  switch (scalar_type) {
+  case ShaderType::ST_float:
+    {
+      float *data = (float *)scratch;
+
+      switch (ptr_data._type) {
+      case ShaderType::ST_int:
+        // Convert int data to float data.
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = (float)(((int *)ptr_data._ptr)[i]);
+          }
+        } else {
+          const int *from_data = (const int *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_uint:
+        // Convert unsigned int data to float data.
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = (float)(((unsigned int *)ptr_data._ptr)[i]);
+          }
+        } else {
+          const unsigned int *from_data = (const unsigned int *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_double:
+        // Downgrade double data to float data.
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = (float)(((double *)ptr_data._ptr)[i]);
+          }
+        } else {
+          const double *from_data = (const double *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_float:
+        if (!pad_rows || num_columns == 4) {
+          // No conversion needed.
+          if (always_copy) {
+            memcpy(data, ptr_data._ptr, total_rows * num_columns * sizeof(float));
+            return data;
+          } else {
+            return (float *)ptr_data._ptr;
+          }
+        } else {
+          const float *from_data = (const float *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      default:
+#ifndef NDEBUG
+        pgraph_cat.error()
+          << "Invalid ShaderPtrData type " << (int)ptr_data._type
+          << " for shader input '" << *id << "'\n";
+#endif
+        return nullptr;
+      }
+
+      return data;
+    }
+    break;
+
+  case ShaderType::ST_int:
+    if (ptr_data._type != ShaderType::ST_int &&
+        ptr_data._type != ShaderType::ST_uint &&
+        ptr_data._type != ShaderType::ST_bool) {
+      pgraph_cat.error()
+        << "Cannot pass floating-point data to integer shader input '" << *id << "'\n";
+      return nullptr;
+    }
+    else if (always_copy) {
+      memcpy(scratch, ptr_data._ptr, total_rows * num_columns * sizeof(int));
+      return scratch;
+    }
+    else {
+      return ptr_data._ptr;
+    }
+    break;
+
+  case ShaderType::ST_uint:
+    if (ptr_data._type != ShaderType::ST_uint &&
+        ptr_data._type != ShaderType::ST_int &&
+        ptr_data._type != ShaderType::ST_bool) {
+      pgraph_cat.error()
+        << "Cannot pass floating-point data to integer shader input '" << *id << "'\n";
+      return nullptr;
+    }
+    else if (always_copy) {
+      memcpy(scratch, ptr_data._ptr, total_rows * num_columns * sizeof(unsigned int));
+      return scratch;
+    }
+    else {
+      return ptr_data._ptr;
+    }
+    break;
+
+  case ShaderType::ST_double:
+    {
+      double *data = (double *)scratch;
+
+      switch (ptr_data._type) {
+      case ShaderType::ST_int:
+        // Convert int data to double data.
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = (double)(((int *)ptr_data._ptr)[i]);
+          }
+        } else {
+          const int *from_data = (const int *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_uint:
+        // Convert int data to double data.
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = (double)(((unsigned int *)ptr_data._ptr)[i]);
+          }
+        } else {
+          const int *from_data = (const int *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_double:
+        if (!pad_rows || num_columns == 4) {
+          // No conversion needed.
+          if (always_copy) {
+            memcpy(data, ptr_data._ptr, total_rows * num_columns * sizeof(double));
+            return data;
+          } else {
+            return (double *)ptr_data._ptr;
+          }
+        } else {
+          const double *from_data = (const double *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_float:
+        // Upgrade float data to double data.
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = (double)(((float *)ptr_data._ptr)[i]);
+          }
+        } else {
+          const float *from_data = (const float *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      default:
+  #ifndef NDEBUG
+        pgraph_cat.error()
+          << "Invalid ShaderPtrData type " << (int)ptr_data._type
+          << " for shader input '" << *id << "'\n";
+  #endif
+        return nullptr;
+      }
+
+      return data;
+    }
+    break;
+
+  case ShaderType::ST_bool:
+    {
+      unsigned int *data = (unsigned int *)scratch;
+
+      switch (ptr_data._type) {
+      case ShaderType::ST_int:
+      case ShaderType::ST_uint:
+      case ShaderType::ST_bool:
+        if (!pad_rows || num_columns == 4) {
+          // No conversion needed.
+          if (always_copy) {
+            memcpy(data, ptr_data._ptr, total_rows * num_columns * sizeof(unsigned int));
+            return data;
+          } else {
+            return (unsigned int *)ptr_data._ptr;
+          }
+        } else {
+          // Pad out rows.
+          const unsigned int *from_data = (const unsigned int *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (*from_data++) != 0;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_double:
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = ((double *)ptr_data._ptr)[i] != 0.0;
+          }
+        } else {
+          const double *from_data = (const double *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (*from_data++) != 0.0;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_float:
+        if (!pad_rows || num_columns == 4) {
+          for (int i = 0; i < total_rows * num_columns; ++i) {
+            data[i] = ((float *)ptr_data._ptr)[i] != 0.0f;
+          }
+        } else {
+          const float *from_data = (const float *)ptr_data._ptr;
+          for (int i = 0; i < total_rows; ++i) {
+            for (int c = 0; c < num_columns; ++c) {
+              data[i * 4 + c] = (*from_data++) != 0.0f;
+            }
+          }
+        }
+        return data;
+
+      default:
+        break;
+      }
+    }
+    break;
+
+  case ShaderType::ST_unknown:
+    break;
+  }
+
+  return nullptr;
+}
+
 /**
  * Returns the ShaderInput as a texture.  Assertion fails if there is none, or
  * if it is not a texture.

+ 6 - 0
panda/src/pgraph/shaderAttrib.h

@@ -122,6 +122,12 @@ PUBLISHED:
   const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const;
   bool get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const;
   const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const;
+  size_t get_shader_input_data(const InternalName *id, void *into,
+                               const ShaderType *type, bool pad_rows) const;
+  void *get_shader_input_data(const InternalName *id, void *scratch,
+                              ShaderType::ScalarType scalar_type, int num_elements,
+                              int num_rows, int num_columns, bool pad_rows,
+                              bool always_copy=false) const;
   ShaderBuffer *get_shader_input_buffer(const InternalName *id) const;
 
 PUBLISHED:

+ 19 - 0
tests/display/test_glsl_shader.py

@@ -781,6 +781,25 @@ def test_glsl_struct_array(gsg):
     })
 
 
+def test_glsl_struct_pseudo_light(gsg):
+    # Something that looks like a named light source, but isn't one at all
+    preamble = """
+    struct FakeLightParameters {
+      vec4 color;
+      vec4 specular;
+    };
+    uniform FakeLightParameters test;
+    """
+    code = """
+    assert(test.color == vec4(1, 2, 3, 4));
+    assert(test.specular == vec4(5, 6, 7, 8));
+    """
+    run_glsl_test(gsg, code, preamble, {
+        'test.color': (1, 2, 3, 4),
+        'test.specular': (5, 6, 7, 8),
+    })
+
+
 def test_glsl_light(gsg):
     preamble = """
     uniform struct p3d_LightSourceParameters {