diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 857493d7a1..76a65877fb 100644 --- a/panda/src/pgraph/renderState.h +++ b/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; diff --git a/panda/src/pgraph/renderState_ext.cxx b/panda/src/pgraph/renderState_ext.cxx index f1aa14615b..79d1c6112b 100644 --- a/panda/src/pgraph/renderState_ext.cxx +++ b/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:: +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 diff --git a/panda/src/pgraph/renderState_ext.h b/panda/src/pgraph/renderState_ext.h index f7854d8646..26b596adc7 100644 --- a/panda/src/pgraph/renderState_ext.h +++ b/panda/src/pgraph/renderState_ext.h @@ -29,6 +29,8 @@ template<> class Extension : public ExtensionBase { public: + static CPT(RenderState) make(PyObject *args, PyObject *kwds); + PyObject *get_composition_cache() const; PyObject *get_invert_composition_cache() const; static PyObject *get_states(); diff --git a/tests/pgraph/test_renderstate.py b/tests/pgraph/test_renderstate.py new file mode 100644 index 0000000000..c7bc3935b8 --- /dev/null +++ b/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()