/* SPDX-FileCopyrightText: 2025 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup pythonintern */ #include "BKE_idtype.hh" #include "BKE_lib_id.hh" #include "BKE_node.hh" #include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_world_types.h" #include "NOD_shader_nodes_inline.hh" #include "bpy_inline_shader_nodes.hh" #include "bpy_rna.hh" #include "../generic/py_capi_utils.hh" extern PyTypeObject bpy_inline_shader_nodes_Type; struct BPy_InlineShaderNodes { PyObject_HEAD bNodeTree *inline_node_tree; }; static BPy_InlineShaderNodes *create_from_shader_node_tree(const bNodeTree &tree) { BPy_InlineShaderNodes *self = reinterpret_cast( bpy_inline_shader_nodes_Type.tp_alloc(&bpy_inline_shader_nodes_Type, 0)); if (!self) { return nullptr; } self->inline_node_tree = blender::bke::node_tree_add_tree( nullptr, (blender::StringRef(tree.id.name) + " Inlined").c_str(), tree.idname); blender::nodes::InlineShaderNodeTreeParams params; blender::nodes::inline_shader_node_tree(tree, *self->inline_node_tree, params); return self; } PyDoc_STRVAR( /* Wrap. */ bpy_inline_shader_nodes_from_material_doc, ".. staticmethod:: from_material(material)\n" "\n" " Create an inlined shader node tree from a material.\n" "\n" " :arg material: The material to inline the node tree of.\n" " :type material: bpy.types.Material\n"); static BPy_InlineShaderNodes *BPy_InlineShaderNodes_static_from_material(PyObject * /*self*/, PyObject *args, PyObject *kwds) { static const char *kwlist[] = {"material", nullptr}; PyObject *py_material; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", const_cast(kwlist), &py_material)) { return nullptr; } ID *material_id = nullptr; if (!pyrna_id_FromPyObject(py_material, &material_id)) { PyErr_Format( PyExc_TypeError, "Expected a Material, not %.200s", Py_TYPE(py_material)->tp_name); return nullptr; } if (GS(material_id->name) != ID_MA) { PyErr_Format(PyExc_TypeError, "Expected a Material, not %.200s", BKE_idtype_idcode_to_name(GS(material_id->name))); return nullptr; } Material *material = blender::id_cast(material_id); if (!material->nodetree) { PyErr_Format(PyExc_TypeError, "Material '%s' has no node tree", BKE_id_name(*material_id)); return nullptr; } return create_from_shader_node_tree(*material->nodetree); } PyDoc_STRVAR( /* Wrap. */ bpy_inline_shader_nodes_from_light_doc, ".. staticmethod:: from_light(light)\n" "\n" " Create an inlined shader node tree from a light.\n" "\n" " :arg light: The light to online the node tree of.\n" " :type light: bpy.types.Light\n"); static BPy_InlineShaderNodes *BPy_InlineShaderNodes_static_from_light(PyObject * /*self*/, PyObject *args, PyObject *kwds) { static const char *kwlist[] = {"light", nullptr}; PyObject *py_light; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", const_cast(kwlist), &py_light)) { return nullptr; } ID *light_id = nullptr; if (!pyrna_id_FromPyObject(py_light, &light_id)) { PyErr_Format(PyExc_TypeError, "Expected a Light, not %.200s", Py_TYPE(py_light)->tp_name); return nullptr; } if (GS(light_id->name) != ID_LA) { PyErr_Format(PyExc_TypeError, "Expected a Light, not %.200s", BKE_idtype_idcode_to_name(GS(light_id->name))); return nullptr; } Light *light = blender::id_cast(light_id); if (!light->nodetree) { PyErr_Format(PyExc_TypeError, "Light '%s' has no node tree", BKE_id_name(*light_id)); return nullptr; } return create_from_shader_node_tree(*light->nodetree); } PyDoc_STRVAR( /* Wrap. */ bpy_inline_shader_nodes_from_world_doc, ".. staticmethod:: from_world(world)\n" "\n" " Create an inlined shader node tree from a world.\n" "\n" " :arg world: The world to inline the node tree of.\n" " :type world: bpy.types.World\n"); static BPy_InlineShaderNodes *BPy_InlineShaderNodes_static_from_world(PyObject * /*self*/, PyObject *args, PyObject *kwds) { static const char *kwlist[] = {"world", nullptr}; PyObject *py_world; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", const_cast(kwlist), &py_world)) { return nullptr; } ID *world_id = nullptr; if (!pyrna_id_FromPyObject(py_world, &world_id)) { PyErr_Format(PyExc_TypeError, "Expected a World, not %.200s", Py_TYPE(py_world)->tp_name); return nullptr; } if (GS(world_id->name) != ID_WO) { PyErr_Format(PyExc_TypeError, "Expected a World, not %.200s", BKE_idtype_idcode_to_name(GS(world_id->name))); return nullptr; } World *world = blender::id_cast(world_id); if (!world->nodetree) { PyErr_Format(PyExc_TypeError, "World '%s' has no node tree", BKE_id_name(*world_id)); return nullptr; } return create_from_shader_node_tree(*world->nodetree); } static void BPy_InlineShaderNodes_dealloc(BPy_InlineShaderNodes *self) { BKE_id_free(nullptr, self->inline_node_tree); Py_TYPE(self)->tp_free(reinterpret_cast(self)); } PyDoc_STRVAR( /* Wrap. */ bpy_inline_shader_nodes_node_tree_doc, "The inlined node tree.\n" "\n" ":type: :class:`bpy.types.NodeTree`\n"); static PyObject *BPy_InlineShaderNodes_get_node_tree(BPy_InlineShaderNodes *self) { return pyrna_id_CreatePyObject(blender::id_cast(self->inline_node_tree)); } static PyGetSetDef BPy_InlineShaderNodes_getseters[] = { {"node_tree", (getter)BPy_InlineShaderNodes_get_node_tree, nullptr, bpy_inline_shader_nodes_node_tree_doc, nullptr}, {nullptr}, }; #ifdef __GNUC__ # ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wcast-function-type" # else # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcast-function-type" # endif #endif static PyMethodDef BPy_InlineShaderNodes_methods[] = { {"from_material", (PyCFunction)BPy_InlineShaderNodes_static_from_material, METH_VARARGS | METH_KEYWORDS | METH_STATIC, bpy_inline_shader_nodes_from_material_doc}, {"from_light", (PyCFunction)BPy_InlineShaderNodes_static_from_light, METH_VARARGS | METH_KEYWORDS | METH_STATIC, bpy_inline_shader_nodes_from_light_doc}, {"from_world", (PyCFunction)BPy_InlineShaderNodes_static_from_world, METH_VARARGS | METH_KEYWORDS | METH_STATIC, bpy_inline_shader_nodes_from_world_doc}, {nullptr}, }; #ifdef __GNUC__ # ifdef __clang__ # pragma clang diagnostic pop # else # pragma GCC diagnostic pop # endif #endif PyDoc_STRVAR( /* Wrap. */ bpy_inline_shader_nodes_doc, "An inlined shader node tree.\n"); PyTypeObject bpy_inline_shader_nodes_Type = { /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0) /*tp_name*/ "InlineShaderNodes", /*tp_basicsize*/ sizeof(BPy_InlineShaderNodes), /*tp_itemsize*/ 0, /*tp_dealloc*/ reinterpret_cast(BPy_InlineShaderNodes_dealloc), /*tp_vectorcall_offset*/ 0, /*tp_getattr*/ nullptr, /*tp_setattr*/ nullptr, /*tp_as_async*/ nullptr, /*tp_repr*/ nullptr, /*tp_as_number*/ nullptr, /*tp_as_sequence*/ nullptr, /*tp_as_mapping*/ nullptr, /*tp_hash*/ nullptr, /*tp_call*/ nullptr, /*tp_str*/ nullptr, /*tp_getattro*/ nullptr, /*tp_setattro*/ nullptr, /*tp_as_buffer*/ nullptr, /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_doc*/ bpy_inline_shader_nodes_doc, /*tp_traverse*/ nullptr, /*tp_clear*/ nullptr, /*tp_richcompare*/ nullptr, /*tp_weaklistoffset*/ 0, /*tp_iter*/ nullptr, /*tp_iternext*/ nullptr, /*tp_methods*/ BPy_InlineShaderNodes_methods, /*tp_members*/ nullptr, /*tp_getset*/ BPy_InlineShaderNodes_getseters, /*tp_base*/ nullptr, /*tp_dict*/ nullptr, /*tp_descr_get*/ nullptr, /*tp_descr_set*/ nullptr, /*tp_dictoffset*/ 0, /*tp_init*/ nullptr, /*tp_alloc*/ nullptr, /*tp_new*/ nullptr, }; PyObject *BPyInit_inline_shader_nodes_type() { if (PyType_Ready(&bpy_inline_shader_nodes_Type) < 0) { return nullptr; } return reinterpret_cast(&bpy_inline_shader_nodes_Type); }