diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index 6659a310db..085d9421d1 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -791,6 +791,15 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _args_type = InterfaceMaker::AT_single_arg; } else { _args_type = InterfaceMaker::AT_varargs; + + // If the arguments are named "args" and "kwargs", we will be directly + // passing the argument tuples to the function. + if (_parameters.size() == first_param + 2 && + _parameters[first_param]._name == "args" && + (_parameters[first_param + 1]._name == "kwargs" || + _parameters[first_param + 1]._name == "kwds")) { + _flags |= F_explicit_args; + } } switch (_type) { @@ -890,7 +899,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak if (_args_type == InterfaceMaker::AT_varargs) { // Every other method can take keyword arguments, if they take more // than one argument. - _args_type = InterfaceMaker::AT_keyword_args; + _args_type |= InterfaceMaker::AT_keyword_args; } } break; diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index 72e18cf09e..b4b8aea6dd 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -100,6 +100,7 @@ public: F_coerce_constructor = 0x1000, F_divide_float = 0x2000, F_hash = 0x4000, + F_explicit_args = 0x8000, }; typedef vector Parameters; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index f24839d331..2b51f87689 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -3453,7 +3453,13 @@ write_function_for_name(ostream &out, Object *obj, max_required_args = collapse_default_remaps(map_sets, max_required_args); } - if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) { + if (remap->_flags & FunctionRemap::F_explicit_args) { + // We have a remap that wants to handle the wrapper itself. + string expected_params; + write_function_instance(out, remap, 0, 0, expected_params, 2, true, true, + args_type, return_flags); + + } else if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) { switch (args_type) { case AT_keyword_args: indent(out, 2) << "int parameter_count = (int)PyTuple_Size(args);\n"; @@ -4536,19 +4542,23 @@ write_function_instance(ostream &out, FunctionRemap *remap, expected_params += methodNameFromCppName(remap, "", false); expected_params += "("; - int num_params = max_num_args; - if (remap->_has_this) { - num_params += 1; - } - if (num_params > (int)remap->_parameters.size()) { - // Limit to how many parameters this remap actually has. - num_params = (int)remap->_parameters.size(); - max_num_args = num_params; + int num_params = 0; + + if ((remap->_flags & FunctionRemap::F_explicit_args) == 0) { + num_params = max_num_args; if (remap->_has_this) { - --max_num_args; + num_params += 1; } + if (num_params > (int)remap->_parameters.size()) { + // Limit to how many parameters this remap actually has. + num_params = (int)remap->_parameters.size(); + max_num_args = num_params; + if (remap->_has_this) { + --max_num_args; + } + } + nassertv(num_params <= (int)remap->_parameters.size()); } - nassertv(num_params <= (int)remap->_parameters.size()); bool only_pyobjects = true; @@ -4584,6 +4594,17 @@ write_function_instance(ostream &out, FunctionRemap *remap, } } + if (remap->_flags & FunctionRemap::F_explicit_args) { + // The function handles the arguments by itself. + expected_params += "*args"; + pexprs.push_back("args"); + if (args_type == AT_keyword_args) { + expected_params += ", **kwargs"; + pexprs.push_back("kwds"); + } + num_params = 0; + } + // Now convert (the rest of the) actual arguments, one by one. for (; pn < num_params; ++pn) { ParameterRemap *param = remap->_parameters[pn]._remap; diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 1bbe0845cf..6aeeda6d01 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -659,6 +659,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); + EXTENSION(void set_shader_inputs(PyObject *args, PyObject *kwargs)); + void clear_shader_input(CPT_InternalName id); void set_instance_count(int instance_count); diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx index 081a69a814..60cbe19a1c 100644 --- a/panda/src/pgraph/nodePath_ext.cxx +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -13,6 +13,7 @@ #include "nodePath_ext.h" #include "typedWritable_ext.h" +#include "shaderAttrib.h" #ifdef HAVE_PYTHON @@ -24,6 +25,35 @@ 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 /** @@ -211,6 +241,192 @@ py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &da return NodePath::decode_from_bam_stream(data, reader); } +/** + * 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. + */ +void Extension:: +set_shader_inputs(PyObject *args, PyObject *kwargs) { + if (PyObject_Size(args) > 0) { + Dtool_Raise_TypeError("NodePath.set_shader_inputs takes only keyword arguments"); + return; + } + + 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()); + } + + 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("NodePath.set_shader_inputs accepts only string keywords"); + return; + } + + CPT_InternalName name(string(buffer, length)); + ShaderInput *input = nullptr; + + 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 = new ShaderInput(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 = new ShaderInput(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 = new ShaderInput(name, (Texture *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) { + input = new ShaderInput(name, *(const NodePath *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) { + input = new ShaderInput(name, *(const PTA_float *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) { + input = new ShaderInput(name, *(const PTA_double *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) { + input = new ShaderInput(name, *(const PTA_int *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) { + input = new ShaderInput(name, *(const PTA_LVecBase4f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) { + input = new ShaderInput(name, *(const PTA_LVecBase3f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) { + input = new ShaderInput(name, *(const PTA_LVecBase2f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) { + input = new ShaderInput(name, *(const PTA_LMatrix4f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) { + input = new ShaderInput(name, *(const PTA_LMatrix3f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) { + input = new ShaderInput(name, *(const PTA_LVecBase4d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) { + input = new ShaderInput(name, *(const PTA_LVecBase3d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) { + input = new ShaderInput(name, *(const PTA_LVecBase2d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) { + input = new ShaderInput(name, *(const PTA_LMatrix4d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) { + input = new ShaderInput(name, *(const PTA_LMatrix3d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) { + input = new ShaderInput(name, *(const PTA_LVecBase4i *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) { + input = new ShaderInput(name, *(const PTA_LVecBase3i *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) { + input = new ShaderInput(name, *(const PTA_LVecBase2i *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) { + input = new ShaderInput(name, *(const LVecBase4f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) { + input = new ShaderInput(name, *(const LVecBase3f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) { + input = new ShaderInput(name, *(const LVecBase2f *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) { + input = new ShaderInput(name, *(const LVecBase4d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) { + input = new ShaderInput(name, *(const LVecBase3d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) { + input = new ShaderInput(name, *(const LVecBase2d *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) { + input = new ShaderInput(name, *(const LVecBase4i *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) { + input = new ShaderInput(name, *(const LVecBase3i *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) { + input = new ShaderInput(name, *(const LVecBase2i *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) { + input = new ShaderInput(name, (ShaderBuffer *)ptr); + + } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) { + input = new ShaderInput(name, (ParamValueBase *)ptr); + + } else { + Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs"); + return; + } + + } else if (PyFloat_Check(value)) { + input = new ShaderInput(name, LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0)); + +#if PY_MAJOR_VERSION < 3 + } else if (PyInt_Check(value)) { + input = new ShaderInput(name, LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0)); +#endif + + } else if (PyLong_Check(value)) { + input = new ShaderInput(name, LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0)); + + } else { + Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs"); + return; + } + + attrib->_inputs[move(name)] = input; + } + + node->set_attrib(ShaderAttrib::return_new(attrib)); +} + /** * Returns the tight bounds as a 2-tuple of LPoint3 objects. This is a * convenience function for Python users, among which the use of diff --git a/panda/src/pgraph/nodePath_ext.h b/panda/src/pgraph/nodePath_ext.h index 3b0b8db42a..44c78378db 100644 --- a/panda/src/pgraph/nodePath_ext.h +++ b/panda/src/pgraph/nodePath_ext.h @@ -49,6 +49,8 @@ public: // This is defined to implement cycle detection in Python tags. INLINE int __traverse__(visitproc visit, void *arg); + void set_shader_inputs(PyObject *args, PyObject *kwargs); + PyObject *get_tight_bounds(const NodePath &other = NodePath()) const; }; diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index 5a28e929b1..ebdcca499c 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -31,6 +31,7 @@ #include "pta_LVecBase4.h" #include "pta_LVecBase3.h" #include "pta_LVecBase2.h" +#include "extension.h" /** * @@ -147,6 +148,8 @@ private: typedef pmap Inputs; Inputs _inputs; + friend class Extension; + PUBLISHED: static int get_class_slot() { return _attrib_slot;