Bladeren bron

pgraph: custom binding for set_shader_input()
Should be faster and more reliable at PTA handling
We no longer need the interrogate perf hack for the first-arg InternalName

Fixes: #161

rdb 8 jaren geleden
bovenliggende
commit
0e2b14cf4c

+ 2 - 70
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -4389,57 +4389,6 @@ write_function_forset(ostream &out,
     std::sort(remaps.begin(), remaps.end(), RemapCompareLess);
     std::vector<FunctionRemap *>::const_iterator sii;
 
-    // Check if all of them have an InternalName pointer as first parameter.
-    // This is a dirty hack, of course, to work around an awkward overload
-    // resolution problem in NodePath::set_shader_input() (while perhaps also
-    // improving its performance).  If I had more time I'd create a better
-    // solution.
-    bool first_internalname = false;
-    string first_pexpr2(first_pexpr);
-    if (first_pexpr.empty() && args_type != AT_no_args) {
-      first_internalname = true;
-
-      for (sii = remaps.begin(); sii != remaps.end(); ++sii) {
-        remap = (*sii);
-        if (remap->_parameters.size() > (size_t)remap->_has_this) {
-          ParameterRemap *param = remap->_parameters[(size_t)remap->_has_this]._remap;
-          string param_name = param->get_orig_type()->get_local_name(&parser);
-
-          if (param_name != "CPT_InternalName" &&
-              param_name != "InternalName const *" &&
-              param_name != "InternalName *") {
-            // Aw.
-            first_internalname = false;
-            break;
-          }
-        } else {
-          first_internalname = false;
-          break;
-        }
-      }
-      if (first_internalname) {
-        // Yeah, all remaps have a first InternalName parameter, so process
-        // that and remove it from the args tuple.
-        if (args_type == AT_single_arg) {
-          // Bit of a weird case, but whatever.
-          indent(out, indent_level) << "PyObject *name_obj = arg;\n";
-          args_type = AT_no_args;
-        } else if (min_num_args == 2 && max_num_args == 2) {
-          indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n";
-          indent(out, indent_level) << "PyObject *arg = PyTuple_GET_ITEM(args, 1);\n";
-          args_type = AT_single_arg;
-        } else {
-          indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n";
-          indent(out, indent_level) << "args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));\n";
-          return_flags |= RF_decref_args;
-        }
-        indent(out, indent_level) << "PT(InternalName) name;\n";
-        indent(out, indent_level) << "if (Dtool_Coerce_InternalName(name_obj, name)) {\n";
-        indent_level += 2;
-        first_pexpr2 = "name";
-      }
-    }
-
     int num_coercion_possible = 0;
     sii = remaps.begin();
     while (sii != remaps.end()) {
@@ -4474,7 +4423,7 @@ write_function_forset(ostream &out,
       write_function_instance(out, remap, min_num_args, max_num_args,
                               expected_params, indent_level + 2,
                               false, false, args_type, return_flags,
-                              check_exceptions, first_pexpr2);
+                              check_exceptions, first_pexpr);
 
       indent(out, indent_level) << "}\n\n";
     }
@@ -4507,28 +4456,11 @@ write_function_forset(ostream &out,
         write_function_instance(out, remap, min_num_args, max_num_args,
                                 ignore_expected_params, indent_level + 2,
                                 true, false, args_type, return_flags,
-                                check_exceptions, first_pexpr2);
+                                check_exceptions, first_pexpr);
 
         indent(out, indent_level) << "}\n\n";
       }
     }
-
-    if (first_internalname) {
-      indent_level -= 2;
-      if (report_errors) {
-        indent(out, indent_level) << "} else {\n";
-
-        string class_name = remap->_cpptype->get_simple_name();
-        ostringstream msg;
-        msg << classNameFromCppName(class_name, false) << "."
-            << methodNameFromCppName(remap, class_name, false)
-            << "() first argument must be str or InternalName";
-
-        error_raise_return(out, indent_level + 2, return_flags,
-                           "TypeError", msg.str());
-      }
-      indent(out, indent_level) << "}\n";
-    }
   } else {
     // There is only one possible overload with this number of parameters.
     // Just call it.

+ 6 - 1
panda/src/pgraph/nodePath.h

@@ -630,9 +630,12 @@ PUBLISHED:
   void clear_shader();
 
   void set_shader_input(ShaderInput input);
-  INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
+
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
+
+public:
+  INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0);
@@ -659,6 +662,8 @@ PUBLISHED:
   INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2=0,
                                PN_stdfloat n3=0, PN_stdfloat n4=0, int priority=0);
 
+PUBLISHED:
+  EXTENSION(void set_shader_input(CPT_InternalName, PyObject *, int priority=0));
   EXTENSION(void set_shader_inputs(PyObject *args, PyObject *kwargs));
 
   void clear_shader_input(CPT_InternalName id);

+ 28 - 173
panda/src/pgraph/nodePath_ext.cxx

@@ -13,6 +13,7 @@
 
 #include "nodePath_ext.h"
 #include "typedWritable_ext.h"
+#include "shaderInput_ext.h"
 #include "shaderAttrib.h"
 
 #ifdef HAVE_PYTHON
@@ -25,35 +26,7 @@ extern struct Dtool_PyTypedObject Dtool_LPoint3d;
 #else
 extern struct Dtool_PyTypedObject Dtool_LPoint3f;
 #endif
-extern struct Dtool_PyTypedObject Dtool_Texture;
 extern struct Dtool_PyTypedObject Dtool_NodePath;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_float;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_double;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_int;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4f;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3f;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2f;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4f;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3f;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4d;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3d;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2d;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4d;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3d;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4i;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3i;
-extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2i;
-extern struct Dtool_PyTypedObject Dtool_LVecBase4f;
-extern struct Dtool_PyTypedObject Dtool_LVecBase3f;
-extern struct Dtool_PyTypedObject Dtool_LVecBase2f;
-extern struct Dtool_PyTypedObject Dtool_LVecBase4d;
-extern struct Dtool_PyTypedObject Dtool_LVecBase3d;
-extern struct Dtool_PyTypedObject Dtool_LVecBase2d;
-extern struct Dtool_PyTypedObject Dtool_LVecBase4i;
-extern struct Dtool_PyTypedObject Dtool_LVecBase3i;
-extern struct Dtool_PyTypedObject Dtool_LVecBase2i;
-extern struct Dtool_PyTypedObject Dtool_ShaderBuffer;
-extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
 #endif  // CPPPARSER
 
 /**
@@ -241,6 +214,28 @@ py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &da
   return NodePath::decode_from_bam_stream(data, reader);
 }
 
+/**
+ * Sets a single shader input.
+ */
+void Extension<NodePath>::
+set_shader_input(CPT_InternalName name, PyObject *value, int priority) {
+  PT(PandaNode) node = _this->node();
+  CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot());
+  PT(ShaderAttrib) attrib;
+  if (prev_attrib == nullptr) {
+    attrib = new ShaderAttrib();
+  } else {
+    attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p());
+  }
+
+  ShaderInput &input = attrib->_inputs[name];
+  invoke_extension(&input).__init__(move(name), value);
+
+  if (!_PyErr_OCCURRED()) {
+    node->set_attrib(ShaderAttrib::return_new(attrib));
+  }
+}
+
 /**
  * Sets multiple shader inputs at the same time.  This can be significantly
  * more efficient if many inputs need to be set at the same time.
@@ -278,153 +273,13 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
     }
 
     CPT_InternalName name(string(buffer, length));
-    ShaderInput input(nullptr, 0);
-
-    if (PyTuple_CheckExact(value)) {
-      // A tuple is interpreted as a vector.
-      Py_ssize_t size = PyTuple_GET_SIZE(value);
-      if (size > 4) {
-        Dtool_Raise_TypeError("NodePath.set_shader_inputs tuple input should not have more than 4 scalars");
-        return;
-      }
-      // If any of them is a float, we are storing it as a float vector.
-      bool is_float = false;
-      for (Py_ssize_t i = 0; i < size; ++i) {
-        if (PyFloat_CheckExact(PyTuple_GET_ITEM(value, i))) {
-          is_float = true;
-          break;
-        }
-      }
-      if (is_float) {
-        LVecBase4 vec(0);
-        for (Py_ssize_t i = 0; i < size; ++i) {
-          vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i));
-        }
-        input = ShaderInput(move(name), vec);
-      } else {
-        LVecBase4i vec(0);
-        for (Py_ssize_t i = 0; i < size; ++i) {
-          vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i));
-        }
-        input = ShaderInput(move(name), vec);
-      }
-
-    } else if (DtoolCanThisBeAPandaInstance(value)) {
-      Dtool_PyInstDef *inst = (Dtool_PyInstDef *)value;
-      void *ptr;
-
-      if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) {
-        input = ShaderInput(move(name), (Texture *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) {
-        input = ShaderInput(move(name), *(const NodePath *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) {
-        input = ShaderInput(move(name), *(const PTA_float *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) {
-        input = ShaderInput(move(name), *(const PTA_double *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) {
-        input = ShaderInput(move(name), *(const PTA_int *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase4f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase3f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase2f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) {
-        input = ShaderInput(move(name), *(const PTA_LMatrix4f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) {
-        input = ShaderInput(move(name), *(const PTA_LMatrix3f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase4d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase3d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase2d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) {
-        input = ShaderInput(move(name), *(const PTA_LMatrix4d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) {
-        input = ShaderInput(move(name), *(const PTA_LMatrix3d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase4i *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase3i *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) {
-        input = ShaderInput(move(name), *(const PTA_LVecBase2i *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) {
-        input = ShaderInput(move(name), *(const LVecBase4f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) {
-        input = ShaderInput(move(name), *(const LVecBase3f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) {
-        input = ShaderInput(move(name), *(const LVecBase2f *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) {
-        input = ShaderInput(move(name), *(const LVecBase4d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) {
-        input = ShaderInput(move(name), *(const LVecBase3d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) {
-        input = ShaderInput(move(name), *(const LVecBase2d *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) {
-        input = ShaderInput(move(name), *(const LVecBase4i *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) {
-        input = ShaderInput(move(name), *(const LVecBase3i *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) {
-        input = ShaderInput(move(name), *(const LVecBase2i *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) {
-        input = ShaderInput(move(name), (ShaderBuffer *)ptr);
-
-      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) {
-        input = ShaderInput(move(name), (ParamValueBase *)ptr);
-
-      } else {
-        Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
-        return;
-      }
-
-    } else if (PyFloat_Check(value)) {
-      input = ShaderInput(move(name), LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0));
-
-#if PY_MAJOR_VERSION < 3
-    } else if (PyInt_Check(value)) {
-      input = ShaderInput(move(name), LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0));
-#endif
-
-    } else if (PyLong_Check(value)) {
-      input = ShaderInput(move(name), LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0));
-
-    } else {
-      Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
-      return;
-    }
-
-    attrib->_inputs[input.get_name()] = move(input);
+    ShaderInput &input = attrib->_inputs[name];
+    invoke_extension(&input).__init__(move(name), value);
   }
 
-  node->set_attrib(ShaderAttrib::return_new(attrib));
+  if (!_PyErr_OCCURRED()) {
+    node->set_attrib(ShaderAttrib::return_new(attrib));
+  }
 }
 
 /**

+ 1 - 0
panda/src/pgraph/nodePath_ext.h

@@ -49,6 +49,7 @@ public:
   // This is defined to implement cycle detection in Python tags.
   INLINE int __traverse__(visitproc visit, void *arg);
 
+  void set_shader_input(CPT_InternalName id, PyObject *value, int priority=0);
   void set_shader_inputs(PyObject *args, PyObject *kwargs);
 
   PyObject *get_tight_bounds(const NodePath &other = NodePath()) const;

+ 2 - 0
panda/src/pgraph/p3pgraph_ext_composite.cxx

@@ -2,4 +2,6 @@
 #include "nodePathCollection_ext.cxx"
 #include "pandaNode_ext.cxx"
 #include "renderState_ext.cxx"
+#include "shaderAttrib_ext.cxx"
+#include "shaderInput_ext.cxx"
 #include "transformState_ext.cxx"

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

@@ -73,6 +73,7 @@ PUBLISHED:
   // Shader Inputs
   CPT(RenderAttrib) set_shader_input(ShaderInput input) const;
 
+public:
   INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, Texture *tex,       int priority=0) const;
   INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0) const;
   INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0) const;
@@ -90,6 +91,10 @@ PUBLISHED:
   INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, double n1=0, double n2=0, double n3=0, double n4=1,
                                             int priority=0) const;
 
+PUBLISHED:
+  EXTENSION(CPT(RenderAttrib) set_shader_input(CPT_InternalName, PyObject *, int priority=0) const);
+  EXTENSION(CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const);
+
   CPT(RenderAttrib) set_instance_count(int instance_count) const;
 
   CPT(RenderAttrib) set_flag(int flag, bool value) const;
@@ -150,6 +155,7 @@ private:
   Inputs _inputs;
 
   friend class Extension<NodePath>;
+  friend class Extension<ShaderAttrib>;
 
 PUBLISHED:
   static int get_class_slot() {

+ 71 - 0
panda/src/pgraph/shaderAttrib_ext.cxx

@@ -0,0 +1,71 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderAttrib_ext.cxx
+ * @author rdb
+ * @date 2017-10-08
+ */
+
+#include "shaderAttrib_ext.h"
+#include "shaderInput_ext.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns a new ShaderAttrib with the given shader input set.
+ */
+CPT(RenderAttrib) Extension<ShaderAttrib>::
+set_shader_input(CPT_InternalName name, PyObject *value, int priority) const {
+  ShaderAttrib *attrib = new ShaderAttrib(*_this);
+
+  ShaderInput &input = attrib->_inputs[name];
+  invoke_extension(&input).__init__(move(name), value);
+
+  return ShaderAttrib::return_new(attrib);
+}
+
+/**
+ * Returns a new ShaderAttrib with the given shader inputs set.  This is a
+ * more efficient way to set multiple shader inputs than calling
+ * set_shader_input multiple times.
+ */
+CPT(RenderAttrib) Extension<ShaderAttrib>::
+set_shader_inputs(PyObject *args, PyObject *kwargs) const {
+  if (PyObject_Size(args) > 0) {
+    Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs takes only keyword arguments");
+    return nullptr;
+  }
+
+  ShaderAttrib *attrib = new ShaderAttrib(*_this);
+
+  PyObject *key, *value;
+  Py_ssize_t pos = 0;
+
+  while (PyDict_Next(kwargs, &pos, &key, &value)) {
+    char *buffer;
+    Py_ssize_t length;
+#if PY_MAJOR_VERSION >= 3
+    buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length);
+    if (buffer == nullptr) {
+#else
+    if (PyString_AsStringAndSize(key, &buffer, &length) == -1) {
+#endif
+      Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs accepts only string keywords");
+      delete attrib;
+      return nullptr;
+    }
+
+    CPT_InternalName name(string(buffer, length));
+    ShaderInput &input = attrib->_inputs[name];
+    invoke_extension(&input).__init__(move(name), value);
+  }
+
+  return ShaderAttrib::return_new(attrib);
+}
+
+#endif  // HAVE_PYTHON

+ 38 - 0
panda/src/pgraph/shaderAttrib_ext.h

@@ -0,0 +1,38 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderAttrib_ext.h
+ * @author rdb
+ * @date 2017-10-08
+ */
+
+#ifndef SHADERATTRIB_EXT_H
+#define SHADERATTRIB_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "shaderAttrib.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for ShaderAttrib, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<ShaderAttrib> : public ExtensionBase<ShaderAttrib> {
+public:
+  CPT(RenderAttrib) set_shader_input(CPT_InternalName id, PyObject *value, int priority=0) const;
+  CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // SHADERATTRIB_EXT_H

+ 8 - 1
panda/src/pgraph/shaderInput.h

@@ -46,7 +46,11 @@ PUBLISHED:
   };
 
   static const ShaderInput &get_blank();
-  INLINE ShaderInput(CPT_InternalName name, int priority=0);
+  INLINE explicit ShaderInput(CPT_InternalName name, int priority=0);
+
+  EXTENSION(explicit ShaderInput(CPT_InternalName name, PyObject *value, int priority=0));
+
+public:
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0);
@@ -83,6 +87,8 @@ PUBLISHED:
   INLINE ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority=0);
 
   ShaderInput(CPT_InternalName name, const NodePath &np, int priority=0);
+
+PUBLISHED:
   ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
   ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority=0);
 
@@ -132,6 +138,7 @@ private:
   int _type;
 
   friend class ShaderAttrib;
+  friend class Extension<ShaderInput>;
 };
 
 #include "shaderInput.I"

+ 539 - 0
panda/src/pgraph/shaderInput_ext.cxx

@@ -0,0 +1,539 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderInput_ext.cxx
+ * @author rdb
+ * @date 2017-10-06
+ */
+
+#include "shaderInput_ext.h"
+#include "paramNodePath.h"
+
+#ifdef HAVE_PYTHON
+
+#ifndef CPPPARSER
+extern struct Dtool_PyTypedObject Dtool_Texture;
+extern struct Dtool_PyTypedObject Dtool_NodePath;
+extern struct Dtool_PyTypedObject Dtool_PointerToVoid;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_float;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_double;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_int;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4i;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3i;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2i;
+extern struct Dtool_PyTypedObject Dtool_LMatrix4f;
+extern struct Dtool_PyTypedObject Dtool_LMatrix3f;
+extern struct Dtool_PyTypedObject Dtool_LMatrix4d;
+extern struct Dtool_PyTypedObject Dtool_LMatrix3d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase4f;
+extern struct Dtool_PyTypedObject Dtool_LVecBase3f;
+extern struct Dtool_PyTypedObject Dtool_LVecBase2f;
+extern struct Dtool_PyTypedObject Dtool_LVecBase4d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase3d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase2d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase4i;
+extern struct Dtool_PyTypedObject Dtool_LVecBase3i;
+extern struct Dtool_PyTypedObject Dtool_LVecBase2i;
+extern struct Dtool_PyTypedObject Dtool_ShaderBuffer;
+extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
+#endif  // CPPPARSER
+
+/**
+ * Sets a shader input from an arbitrary Python object.
+ */
+void Extension<ShaderInput>::
+__init__(CPT_InternalName name, PyObject *value, int priority) {
+  _this->_name = move(name);
+  _this->_priority = priority;
+
+  if (PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value) <= 4) {
+    // A tuple is interpreted as a vector.
+    Py_ssize_t size = PyTuple_GET_SIZE(value);
+
+    // If any of them is a float, we are storing it as a float vector.
+    bool is_float = false;
+    for (Py_ssize_t i = 0; i < size; ++i) {
+      if (PyFloat_CheckExact(PyTuple_GET_ITEM(value, i))) {
+        is_float = true;
+        break;
+      }
+    }
+    if (is_float) {
+      LVecBase4 vec(0);
+      for (Py_ssize_t i = 0; i < size; ++i) {
+        vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i));
+      }
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector = vec;
+    } else {
+      LVecBase4i vec(0);
+      for (Py_ssize_t i = 0; i < size; ++i) {
+        vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i));
+      }
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector = LCAST(PN_stdfloat, vec);
+    }
+
+  } else if (DtoolCanThisBeAPandaInstance(value)) {
+    Dtool_PyInstDef *inst = (Dtool_PyInstDef *)value;
+    void *ptr;
+
+    if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) {
+      _this->_type = ShaderInput::M_texture;
+      _this->_value = (Texture *)ptr;
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) {
+      _this->_type = ShaderInput::M_nodepath;
+      _this->_value = new ParamNodePath(*(const NodePath *)ptr);
+
+    } else if (inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToVoid)) {
+      if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_float *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_double *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_int *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase4f *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase3f *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase2f *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LMatrix4f *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LMatrix3f *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase4d *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase3d *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase2d *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LMatrix4d *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LMatrix3d *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase4i *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase3i *)ptr;
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) {
+        _this->_type = ShaderInput::M_numeric;
+        _this->_stored_ptr = *(const PTA_LVecBase2i *)ptr;
+
+      } else {
+        Dtool_Raise_TypeError("unknown type passed to ShaderInput");
+        return;
+      }
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix4f))) {
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = *(const LMatrix4f *)ptr;
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix3f))) {
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = *(const LMatrix3f *)ptr;
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix4d))) {
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = *(const LMatrix4d *)ptr;
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LMatrix3d))) {
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = *(const LMatrix3d *)ptr;
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) {
+      const LVecBase4f &vec = *(const LVecBase4f *)ptr;
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector = LCAST(PN_stdfloat, vec);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) {
+      const LVecBase3f &vec = *(const LVecBase3f *)ptr;
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector.set(vec[0], vec[1], vec[2], 0);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) {
+      const LVecBase2f &vec = *(const LVecBase2f *)ptr;
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector.set(vec[0], vec[1], 0, 0);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) {
+      const LVecBase4d &vec = *(const LVecBase4d *)ptr;
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector = LCAST(PN_stdfloat, vec);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) {
+      const LVecBase3d &vec = *(const LVecBase3d *)ptr;
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector.set(vec[0], vec[1], vec[2], 0);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) {
+      const LVecBase2d &vec = *(const LVecBase2d *)ptr;
+      _this->_type = ShaderInput::M_vector;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector.set(vec[0], vec[1], 0, 0);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) {
+      const LVecBase4i &vec = *(const LVecBase4i *)ptr;
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector = LCAST(PN_stdfloat, vec);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) {
+      const LVecBase3i &vec = *(const LVecBase3i *)ptr;
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector.set(vec[0], vec[1], vec[2], 0);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) {
+      const LVecBase2i &vec = *(const LVecBase2i *)ptr;
+      _this->_type = ShaderInput::M_numeric;
+      _this->_stored_ptr = vec;
+      _this->_stored_vector.set(vec[0], vec[1], 0, 0);
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) {
+      _this->_type = ShaderInput::M_buffer;
+      _this->_value = (ShaderBuffer *)ptr;
+
+    } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) {
+      _this->_type = ShaderInput::M_param;
+      _this->_value = (ParamValueBase *)ptr;
+
+    } else {
+      Dtool_Raise_TypeError("unknown type passed to ShaderInput");
+      return;
+    }
+
+  } else if (PyFloat_Check(value)) {
+    LVecBase4 vec(PyFloat_AS_DOUBLE(value), 0, 0, 0);
+    _this->_type = ShaderInput::M_vector;
+    _this->_stored_ptr = vec;
+    _this->_stored_vector = vec;
+
+#if PY_MAJOR_VERSION < 3
+  } else if (PyInt_Check(value)) {
+    LVecBase4i vec((int)PyInt_AS_LONG(value), 0, 0, 0);
+    _this->_type = ShaderInput::M_numeric;
+    _this->_stored_ptr = vec;
+    _this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0);
+#endif
+
+  } else if (PyLong_Check(value)) {
+    LVecBase4i vec((int)PyLong_AsLong(value), 0, 0, 0);
+    _this->_type = ShaderInput::M_numeric;
+    _this->_stored_ptr = vec;
+    _this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0);
+
+  } else if (PySequence_Check(value) && !PyUnicode_CheckExact(value)) {
+    // Iterate over the sequence to make sure all have the same type.
+    PyObject *fast = PySequence_Fast(value, "unknown type passed to ShaderInput");
+    if (fast == nullptr) {
+      return;
+    }
+
+    Py_ssize_t num_items = PySequence_Fast_GET_SIZE(value);
+    if (num_items <= 0) {
+      // We can't determine the type of a list of size 0.
+      _this->_type = ShaderInput::M_numeric;
+      Py_DECREF(fast);
+      return;
+    }
+
+    bool has_float = false;
+    Py_ssize_t known_itemsize = -1;
+
+    PyObject **items = PySequence_Fast_ITEMS(fast);
+    for (Py_ssize_t i = 0; i < num_items; ++i) {
+      PyObject *item = items[i];
+
+      if (PySequence_Check(item)) {
+        Py_ssize_t itemsize = PySequence_Size(item);
+        if (known_itemsize >= 0 && itemsize != known_itemsize) {
+          Dtool_Raise_TypeError("inconsistent sequence length among elements of sequence passed to ShaderInput");
+          Py_DECREF(fast);
+          return;
+        }
+        known_itemsize = itemsize;
+
+        // Check their types.
+        for (Py_ssize_t j = 0; j < itemsize; ++j) {
+          PyObject *subitem = PySequence_ITEM(item, j);
+          if (PyFloat_CheckExact(subitem)) {
+            Py_DECREF(subitem);
+            has_float = true;
+            break;
+          } else if (PyLongOrInt_Check(subitem)) {
+          } else {
+            Dtool_Raise_TypeError("unknown element type in sequence passed as element of sequence passed to ShaderInput");
+            Py_DECREF(subitem);
+            Py_DECREF(fast);
+            break;
+          }
+          Py_DECREF(subitem);
+        }
+      } else if (PyFloat_CheckExact(item)) {
+        has_float = true;
+      } else if (PyLongOrInt_Check(item)) {
+      } else {
+        Dtool_Raise_TypeError("unknown element type in sequence passed to ShaderInput");
+        Py_DECREF(fast);
+        return;
+      }
+    }
+
+    // Now that we have verified the dimensions and type of the PTA, we can
+    // read in the actual elements.
+    switch (known_itemsize) {
+    case -1:
+      if (has_float) {
+        PTA_float pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          pta.push_back(PyFloat_AsDouble(items[i]));
+        }
+        _this->_stored_ptr = pta;
+      } else {
+        PTA_int pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          pta.push_back((int)PyLongOrInt_AS_LONG(items[i]));
+        }
+        _this->_stored_ptr = pta;
+      }
+      break;
+
+    case 1:
+      if (has_float) {
+        PTA_float pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem = PySequence_ITEM(item, 0);
+            pta.push_back(PyFloat_AsDouble(subitem));
+            Py_DECREF(subitem);
+          } else {
+            pta.push_back(PyFloat_AsDouble(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      } else {
+        PTA_int pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem = PySequence_ITEM(item, 0);
+            pta.push_back((int)PyLongOrInt_AS_LONG(subitem));
+            Py_DECREF(subitem);
+          } else {
+            pta.push_back((int)PyLongOrInt_AS_LONG(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      }
+      break;
+
+    case 2:
+      if (has_float) {
+        PTA_LVecBase2f pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem0 = PySequence_ITEM(item, 0);
+            PyObject *subitem1 = PySequence_ITEM(item, 1);
+            pta.push_back(LVecBase2f(PyFloat_AsDouble(subitem0),
+                                     PyFloat_AsDouble(subitem1)));
+            Py_DECREF(subitem0);
+            Py_DECREF(subitem1);
+          } else {
+            pta.push_back(PyFloat_AsDouble(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      } else {
+        PTA_LVecBase2i pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem0 = PySequence_ITEM(item, 0);
+            PyObject *subitem1 = PySequence_ITEM(item, 1);
+            pta.push_back(LVecBase2i((int)PyLongOrInt_AS_LONG(subitem0),
+                                     (int)PyLongOrInt_AS_LONG(subitem1)));
+            Py_DECREF(subitem0);
+            Py_DECREF(subitem1);
+          } else {
+            pta.push_back((int)PyLongOrInt_AS_LONG(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      }
+      break;
+
+    case 3:
+      if (has_float) {
+        PTA_LVecBase3f pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem0 = PySequence_ITEM(item, 0);
+            PyObject *subitem1 = PySequence_ITEM(item, 1);
+            PyObject *subitem2 = PySequence_ITEM(item, 2);
+            pta.push_back(LVecBase3f(PyFloat_AsDouble(subitem0),
+                                     PyFloat_AsDouble(subitem1),
+                                     PyFloat_AsDouble(subitem2)));
+            Py_DECREF(subitem0);
+            Py_DECREF(subitem1);
+            Py_DECREF(subitem2);
+          } else {
+            pta.push_back(PyFloat_AsDouble(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      } else {
+        PTA_LVecBase3i pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem0 = PySequence_ITEM(item, 0);
+            PyObject *subitem1 = PySequence_ITEM(item, 1);
+            PyObject *subitem2 = PySequence_ITEM(item, 2);
+            pta.push_back(LVecBase3i((int)PyLongOrInt_AS_LONG(subitem0),
+                                     (int)PyLongOrInt_AS_LONG(subitem1),
+                                     (int)PyLongOrInt_AS_LONG(subitem2)));
+            Py_DECREF(subitem0);
+            Py_DECREF(subitem1);
+            Py_DECREF(subitem2);
+          } else {
+            pta.push_back((int)PyLongOrInt_AS_LONG(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      }
+      break;
+
+    case 4:
+      if (has_float) {
+        PTA_LVecBase4f pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem0 = PySequence_ITEM(item, 0);
+            PyObject *subitem1 = PySequence_ITEM(item, 1);
+            PyObject *subitem2 = PySequence_ITEM(item, 2);
+            PyObject *subitem3 = PySequence_ITEM(item, 3);
+            pta.push_back(LVecBase4f(PyFloat_AsDouble(subitem0),
+                                     PyFloat_AsDouble(subitem1),
+                                     PyFloat_AsDouble(subitem2),
+                                     PyFloat_AsDouble(subitem3)));
+            Py_DECREF(subitem0);
+            Py_DECREF(subitem1);
+            Py_DECREF(subitem2);
+            Py_DECREF(subitem3);
+          } else {
+            pta.push_back(PyFloat_AsDouble(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      } else {
+        PTA_LVecBase4i pta;
+        pta.reserve(num_items);
+        for (Py_ssize_t i = 0; i < num_items; ++i) {
+          PyObject *item = items[i];
+          if (PySequence_Check(item)) {
+            PyObject *subitem0 = PySequence_ITEM(item, 0);
+            PyObject *subitem1 = PySequence_ITEM(item, 1);
+            PyObject *subitem2 = PySequence_ITEM(item, 2);
+            PyObject *subitem3 = PySequence_ITEM(item, 3);
+            pta.push_back(LVecBase4i((int)PyLongOrInt_AS_LONG(subitem0),
+                                     (int)PyLongOrInt_AS_LONG(subitem1),
+                                     (int)PyLongOrInt_AS_LONG(subitem2),
+                                     (int)PyLongOrInt_AS_LONG(subitem3)));
+            Py_DECREF(subitem0);
+            Py_DECREF(subitem1);
+            Py_DECREF(subitem2);
+            Py_DECREF(subitem3);
+          } else {
+            pta.push_back((int)PyLongOrInt_AS_LONG(item));
+          }
+        }
+        _this->_stored_ptr = pta;
+      }
+      break;
+
+    case 0:
+      Dtool_Raise_TypeError("sequence passed to ShaderInput contains an empty sequence");
+      break;
+
+    default:
+      Dtool_Raise_TypeError("sequence passed to ShaderInput contains a sequence of more than 4 elements");
+      break;
+    }
+
+    _this->_type = ShaderInput::M_numeric;
+
+    Py_DECREF(fast);
+
+  } else {
+    Dtool_Raise_TypeError("unknown type passed to ShaderInput");
+  }
+}
+
+#endif  // HAVE_PYTHON

+ 37 - 0
panda/src/pgraph/shaderInput_ext.h

@@ -0,0 +1,37 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderInput_ext.h
+ * @author rdb
+ * @date 2017-10-06
+ */
+
+#ifndef SHADERINPUT_EXT_H
+#define SHADERINPUT_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "shaderInput.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for NodePath, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<ShaderInput> : public ExtensionBase<ShaderInput> {
+public:
+  void __init__(CPT_InternalName name, PyObject *value, int priority=0);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // SHADERINPUT_EXT_H