Jelajahi Sumber

pgraph: Support any number of attribs in `RenderState::make()` in Python

rdb 3 tahun lalu
induk
melakukan
63017864ab

+ 4 - 0
panda/src/pgraph/renderState.h

@@ -71,6 +71,9 @@ PUBLISHED:
   bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const;
 
   INLINE static CPT(RenderState) make_empty();
+  EXTENSION(static explicit CPT(RenderState) make(PyObject *args, PyObject *kwargs));
+
+public:
   static CPT(RenderState) make(const RenderAttrib *attrib, int override = 0);
   static CPT(RenderState) make(const RenderAttrib *attrib1,
                                const RenderAttrib *attrib2, int override = 0);
@@ -89,6 +92,7 @@ PUBLISHED:
   static CPT(RenderState) make(const RenderAttrib * const *attrib,
                                int num_attribs, int override = 0);
 
+PUBLISHED:
   CPT(RenderState) compose(const RenderState *other) const;
   CPT(RenderState) invert_compose(const RenderState *other) const;
 

+ 58 - 0
panda/src/pgraph/renderState_ext.cxx

@@ -15,6 +15,64 @@
 
 #ifdef HAVE_PYTHON
 
+/**
+ * Returns a RenderState with a given number of attributes set.
+ */
+CPT(RenderState) Extension<RenderState>::
+make(PyObject *args, PyObject *kwds) {
+  extern struct Dtool_PyTypedObject Dtool_RenderAttrib;
+
+  Py_ssize_t num_attribs = PyTuple_GET_SIZE(args);
+
+  // Check for the override keyword argument.
+  PyObject *py_override = nullptr;
+  if (kwds != nullptr && PyDict_GET_SIZE(kwds) > 0) {
+    if (PyDict_GET_SIZE(kwds) > 1) {
+      Dtool_Raise_TypeError("RenderState.make() received an unexpected keyword argument");
+      return nullptr;
+    }
+
+    PyObject *key;
+    Py_ssize_t ppos = 0;
+    if (!PyDict_Next(kwds, &ppos, &key, &py_override)) {
+      return nullptr;
+    }
+
+    // We got the item, we just need to make sure that it had the right key.
+    if (!PyUnicode_CheckExact(key) || !_PyUnicode_EqualToASCIIString(key, "override")) {
+      Dtool_Raise_TypeError("RenderState.make() received an unexpected keyword argument");
+      return nullptr;
+    }
+
+    if (!PyLong_Check(py_override)) {
+      Dtool_Raise_TypeError("RenderState.make() override argument should be int");
+      return nullptr;
+    }
+  }
+  else if (num_attribs > 1 && PyLong_Check(PyTuple_GET_ITEM(args, num_attribs - 1))) {
+    // It was specified as last positional argument.
+    py_override = PyTuple_GET_ITEM(args, --num_attribs);
+  }
+
+  int override = 0;
+  if (py_override != nullptr) {
+    override = _PyLong_AsInt(py_override);
+    if (override == -1 && _PyErr_OCCURRED()) {
+      return nullptr;
+    }
+  }
+
+  const RenderAttrib **attribs = (const RenderAttrib **)alloca(sizeof(RenderAttrib *) * num_attribs);
+  for (Py_ssize_t i = 0; i < num_attribs; ++i) {
+    PyObject *arg = PyTuple_GET_ITEM(args, i);
+    if (!DtoolInstance_GetPointer(arg, attribs[i], Dtool_RenderAttrib)) {
+      Dtool_Raise_ArgTypeError(arg, i, "RenderState.make", "RenderAttrib");
+      return nullptr;
+    }
+  }
+  return RenderState::make(attribs, num_attribs, override);
+}
+
 /**
  * Returns a list of 2-tuples that represents the composition cache.  For each
  * tuple in the list, the first element is the source render, and the second

+ 2 - 0
panda/src/pgraph/renderState_ext.h

@@ -29,6 +29,8 @@
 template<>
 class Extension<RenderState> : public ExtensionBase<RenderState> {
 public:
+  static CPT(RenderState) make(PyObject *args, PyObject *kwds);
+
   PyObject *get_composition_cache() const;
   PyObject *get_invert_composition_cache() const;
   static PyObject *get_states();

+ 21 - 0
tests/pgraph/test_renderstate.py

@@ -0,0 +1,21 @@
+from panda3d.core import RenderState, TransparencyAttrib, ColorAttrib
+import pytest
+
+
+def test_renderstate_make():
+    assert RenderState.make() == RenderState.make_empty()
+    assert RenderState.make(override=123) == RenderState.make_empty()
+
+    with pytest.raises(TypeError):
+        RenderState.make(override=0, blargh=123)
+        RenderState.make(blargh=123)
+
+    with pytest.raises(OverflowError):
+        RenderState.make(override=0x80000000)
+        RenderState.make(override=-0x80000000)
+
+    state = RenderState.make(ColorAttrib.make_vertex(), TransparencyAttrib.make_default())
+    assert state.has_attrib(ColorAttrib)
+    assert state.has_attrib(TransparencyAttrib)
+    assert state.attribs[ColorAttrib] == ColorAttrib.make_vertex()
+    assert state.attribs[TransparencyAttrib] == TransparencyAttrib.make_default()