/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/python/gpu/gpu_py_shader.c * \ingroup bpygpu * * - Use ``bpygpu_`` for local API. * - Use ``BPyGPU`` for public API. */ #include #include "BLI_utildefines.h" #include "GPU_shader.h" #include "GPU_shader_interface.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" #include "gpu_py_shader.h" /* own include */ /* -------------------------------------------------------------------- */ /** \name Enum Conversion. * \{ */ static void bpygpu_shader_add_enum_objects(PyObject *submodule) { PyObject *dict = PyModule_GetDict(submodule); PyObject *item; #define PY_DICT_ADD_INT(x) PyDict_SetItemString(dict, #x, item = PyLong_FromLong(x)); Py_DECREF(item) /* Shaders */ PY_DICT_ADD_INT(GPU_SHADER_2D_UNIFORM_COLOR); PY_DICT_ADD_INT(GPU_SHADER_2D_FLAT_COLOR); PY_DICT_ADD_INT(GPU_SHADER_2D_SMOOTH_COLOR); PY_DICT_ADD_INT(GPU_SHADER_3D_UNIFORM_COLOR); PY_DICT_ADD_INT(GPU_SHADER_3D_FLAT_COLOR); PY_DICT_ADD_INT(GPU_SHADER_3D_SMOOTH_COLOR); #undef PY_DICT_ADD_INT } static int bpygpu_pyLong_as_shader_enum(PyObject *o) { uint id = (uint)PyLong_AsUnsignedLong(o); if (id >= GPU_NUM_BUILTIN_SHADERS) { PyErr_SetString(PyExc_ValueError, "not a builtin shader identifier"); return -1; } return (int)id; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Shader Type * \{ */ static PyObject *bpygpu_shader_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { static const char *kwlist[] = { "vertexcode", "fragcode", "geocode", "libcode", "defines", NULL}; struct { const char *vertexcode; const char *fragcode; const char *geocode; const char *libcode; const char *defines; } params = {0}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "ss|$sss:GPUShader.__new__", (char **)kwlist, ¶ms.vertexcode, ¶ms.fragcode, ¶ms.geocode, ¶ms.libcode, ¶ms.defines)) { return NULL; } GPUShader *shader = GPU_shader_create( params.vertexcode, params.fragcode, params.geocode, params.libcode, params.defines, NULL); if (shader == NULL) { PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details"); return NULL; } return BPyGPUShader_CreatePyObject(shader); } PyDoc_STRVAR(bpygpu_shader_bind_doc, ".. method:: bind()\n" "\n" " Bind the Shader object.\n" ); static PyObject *bpygpu_shader_bind(BPyGPUShader *self) { GPU_shader_bind(self->shader); Py_RETURN_NONE; } PyDoc_STRVAR(bpygpu_shader_transform_feedback_enable_doc, ".. method:: transform_feedback_enable(vbo_id)\n" "\n" " Start transform feedback operation.\n" "\n" " :return: true if transform feedback was succesfully enabled.\n" " :rtype: `bool`\n" ); static PyObject *bpygpu_shader_transform_feedback_enable( BPyGPUShader *self, PyObject *arg) { uint vbo_id; if ((vbo_id = PyC_Long_AsU32(arg)) == (uint)-1) { return NULL; } return PyBool_FromLong(GPU_shader_transform_feedback_enable(self->shader, vbo_id)); } PyDoc_STRVAR(bpygpu_shader_transform_feedback_disable_doc, ".. method:: transform_feedback_disable()\n" "\n" " Disable transform feedback.\n" ); static PyObject *bpygpu_transform_feedback_disable(BPyGPUShader *self) { GPU_shader_transform_feedback_disable(self->shader); Py_RETURN_NONE; } PyDoc_STRVAR(bpygpu_shader_uniform_from_name_doc, ".. method:: uniform_from_name(name)\n" "\n" " Get uniform location by name.\n" "\n" " :param name: name of the uniform variable whose location is to be queried.\n" " :type name: `str`\n" " :return: the location of the uniform variable.\n" " :rtype: `int`\n" ); static PyObject *bpygpu_shader_uniform_from_name( BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); if (name == NULL) { return NULL; } int uniform = GPU_shader_get_uniform(self->shader, name); if (uniform == -1) { PyErr_SetString(PyExc_SyntaxError, "GPUShader.get_uniform: uniform not found."); return NULL; } return PyLong_FromLong(uniform); } PyDoc_STRVAR(bpygpu_shader_uniform_block_from_name_doc, ".. method:: uniform_block_from_name(name)\n" "\n" " Get uniform block location by name.\n" "\n" " :param name: name of the uniform block variable whose location is to be queried.\n" " :type name: `str`\n" " :return: the location of the uniform block variable.\n" " :rtype: `int`\n" ); static PyObject *bpygpu_shader_uniform_block_from_name( BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); if (name == NULL) { return NULL; } int uniform = GPU_shader_get_uniform_block(self->shader, name); if (uniform == -1) { PyErr_SetString(PyExc_SyntaxError, "GPUShader.get_uniform_block: uniform not found"); return NULL; } return PyLong_FromLong(uniform); } static bool bpygpu_shader_uniform_vector_imp( PyObject *args, int elem_size, int *r_location, int *r_length, int *r_count, Py_buffer *r_pybuffer) { PyObject *buffer; *r_count = 1; if (!PyArg_ParseTuple( args, "iOi|i:GPUShader.uniform_vector_*", r_location, &buffer, r_length, r_count)) { return false; } if (PyObject_GetBuffer(buffer, r_pybuffer, PyBUF_SIMPLE) == -1) { /* PyObject_GetBuffer raise a PyExc_BufferError */ return false; } if (r_pybuffer->len != (*r_length * *r_count * elem_size)) { PyErr_SetString( PyExc_BufferError, "GPUShader.uniform_vector_*: buffer size does not match."); return false; } return true; } PyDoc_STRVAR(bpygpu_shader_uniform_vector_float_doc, ".. method:: uniform_vector_float(location, buffer, length, count)\n" "\n" " Set the buffer to fill the uniform.\n" "\n" " :param location: location of the uniform variable to be modified.\n" " :type location: `int`\n" " :param buffer: buffer object with format float.\n" " :type buffer: `buffer object`\n" " :param length: size of the uniform data type:\n" " 1: float\n" " 2: vec2 or float[2]\n" " 3: vec3 or float[3]\n" " 4: vec4 or float[4]\n" " 9: mat3\n" " 16: mat4\n" " :type length: `int`\n" " :param count: specifies the number of elements, vector or matrices that are to be modified.\n" " :type count: `int`\n" ); static PyObject *bpygpu_shader_uniform_vector_float( BPyGPUShader *self, PyObject *args) { int location, length, count; Py_buffer pybuffer; if (!bpygpu_shader_uniform_vector_imp( args, sizeof(float), &location, &length, &count, &pybuffer)) { return NULL; } GPU_shader_uniform_vector( self->shader, location, length, count, pybuffer.buf); PyBuffer_Release(&pybuffer); Py_RETURN_NONE; } PyDoc_STRVAR(bpygpu_shader_uniform_vector_int_doc, ".. method:: uniform_vector_int(location, buffer, length, count)\n" "\n" " See GPUShader.uniform_vector_float(...) description.\n." ); static PyObject *bpygpu_shader_uniform_vector_int( BPyGPUShader *self, PyObject *args) { int location, length, count; Py_buffer pybuffer; if (!bpygpu_shader_uniform_vector_imp( args, sizeof(int), &location, &length, &count, &pybuffer)) { return NULL; } GPU_shader_uniform_vector_int( self->shader, location, length, count, pybuffer.buf); PyBuffer_Release(&pybuffer); Py_RETURN_NONE; } PyDoc_STRVAR(bpygpu_shader_uniform_int_doc, ".. method:: uniform_int(location, value)\n" "\n" " Set uniform value.\n" "\n" " :param location: builtin identifier.\n" " :type location: `int`\n" " :param value: builtin identifier.\n" " :type value: `int`\n" ); static PyObject *bpygpu_shader_uniform_int( BPyGPUShader *self, PyObject *args) { int location, value; if (!PyArg_ParseTuple( args, "ii:GPUShader.uniform_int", &location, &value)) { return NULL; } GPU_shader_uniform_int(self->shader, location, value); Py_RETURN_NONE; } PyDoc_STRVAR(bpygpu_shader_attr_from_name_doc, ".. method:: attr_from_name(name)\n" "\n" " Get attribute location by name.\n" "\n" " :param name: the name of the attribute variable whose location is to be queried.\n" " :type name: `str`\n" " :return: the location of an attribute variable.\n" " :rtype: `int`\n" ); static PyObject *bpygpu_shader_attr_from_name( BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); if (name == NULL) { return NULL; } int attrib = GPU_shader_get_attribute(self->shader, name); if (attrib == -1) { PyErr_SetString(PyExc_SyntaxError, "GPUShader.attr_from_name: attribute not found."); return NULL; } return PyLong_FromLong(attrib); } PyDoc_STRVAR(bpygpu_shader_program_doc, ".. method:: The name of the program object for use by the OpenGL API (read-only).\n" "\n" " :rtype: `int`\n" ); static PyObject *bpygpu_shader_program_get(BPyGPUShader *self, void *UNUSED(closure)) { return PyLong_FromLong(GPU_shader_get_program(self->shader)); } static struct PyMethodDef bpygpu_shader_methods[] = { {"bind", (PyCFunction)bpygpu_shader_bind, METH_NOARGS, bpygpu_shader_bind_doc}, {"transform_feedback_enable", (PyCFunction)bpygpu_shader_transform_feedback_enable, METH_O, bpygpu_shader_transform_feedback_enable_doc}, {"transform_feedback_disable", (PyCFunction)bpygpu_transform_feedback_disable, METH_NOARGS, bpygpu_shader_transform_feedback_disable_doc}, {"uniform_from_name", (PyCFunction)bpygpu_shader_uniform_from_name, METH_O, bpygpu_shader_uniform_from_name_doc}, {"uniform_block_from_name", (PyCFunction)bpygpu_shader_uniform_block_from_name, METH_O, bpygpu_shader_uniform_block_from_name_doc}, {"uniform_vector_float", (PyCFunction)bpygpu_shader_uniform_vector_float, METH_VARARGS, bpygpu_shader_uniform_vector_float_doc}, {"uniform_vector_int", (PyCFunction)bpygpu_shader_uniform_vector_int, METH_VARARGS, bpygpu_shader_uniform_vector_int_doc}, {"uniform_int", (PyCFunction)bpygpu_shader_uniform_int, METH_VARARGS, bpygpu_shader_uniform_int_doc}, {"attr_from_name", (PyCFunction)bpygpu_shader_attr_from_name, METH_O, bpygpu_shader_attr_from_name_doc}, {NULL, NULL, 0, NULL} }; static PyGetSetDef bpygpu_shader_getseters[] = { {"program", (getter)bpygpu_shader_program_get, (setter)NULL, bpygpu_shader_program_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; static void bpygpu_shader_dealloc(BPyGPUShader *self) { GPU_shader_free(self->shader); Py_TYPE(self)->tp_free((PyObject *)self); } PyDoc_STRVAR(bpygpu_shader_doc, "GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None)\n" "\n" "GPUShader combines multiple GLSL shaders into a program used for drawing.\n" "It must contain a vertex and fragment shaders, with an optional geometry shader.\n" "\n" "The GLSL #version directive is automatically included at the top of shaders, and set to 330.\n" "\n" "Some preprocessor directives are automatically added according to the Operating System or availability.\n" "These are:\n" " \"#define GPU_ATI\\n\"\n" " \"#define GPU_NVIDIA\\n\"\n" " \"#define GPU_INTEL\\n\"\n" "\n" "The following extensions are enabled by default if supported by the GPU:\n" " \"#extension GL_ARB_texture_gather: enable\\n\"\n" " \"#extension GL_ARB_texture_query_lod: enable\\n\"\n" "\n" "To debug shaders, use the --debug-gpu-shaders command line option" " to see full GLSL shader compilation and linking errors.\n" "\n" " :param vertexcode: vertex Shader Code.\n" " :type vertexcode: `str`\n" " :param fragcode: fragment Shader Code.\n" " :type value: `str`\n" " :param geocode: geometry Shader Code.\n" " :type value: `str`\n" " :param libcode: code with functions and presets to be shared between shaders.\n" " :type value: `str`\n" " :param defines: preprocessor directives.\n" " :type value: `str`\n" ); PyTypeObject BPyGPUShader_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "GPUShader", .tp_basicsize = sizeof(BPyGPUShader), .tp_dealloc = (destructor)bpygpu_shader_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = bpygpu_shader_doc, .tp_methods = bpygpu_shader_methods, .tp_getset = bpygpu_shader_getseters, .tp_new = bpygpu_shader_new, }; /** \} */ /* -------------------------------------------------------------------- */ /** \name gpu.shader Module API * \{ */ PyDoc_STRVAR(bpygpu_shader_unbind_doc, ".. function:: unbind()\n" "\n" " Unbind the bound shader object.\n" ); static PyObject *bpygpu_shader_unbind(BPyGPUShader *UNUSED(self)) { GPU_shader_unbind(); Py_RETURN_NONE; } PyDoc_STRVAR(bpygpu_shader_from_builtin_doc, ".. function:: shader_from_builtin(shader_id)\n" "\n" " :param shader_id: shader identifier.\n" " :type shader_id: `int`\n" ); static PyObject *bpygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg) { int shader_id = bpygpu_pyLong_as_shader_enum(arg); if (shader_id == -1) { return NULL; } GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); return BPyGPUShader_CreatePyObject(shader); } PyDoc_STRVAR(bpygpu_shader_code_from_builtin_doc, ".. function:: shader_code_from_builtin(shader_id)\n" "\n" " :param shader_id: shader identifier.\n" " :type shader_id: `int`\n" " :return: vertex, fragment and geometry shader codes.\n" " :rtype: `dict`\n" ); static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyObject *arg) { const char *vert; const char *frag; const char *geom; const char *defines; PyObject *item, *r_dict; int shader_id = bpygpu_pyLong_as_shader_enum(arg); if (shader_id == -1) { return NULL; } GPU_shader_get_builtin_shader_code( shader_id, &vert, &frag, &geom, &defines); r_dict = PyDict_New(); PyDict_SetItemString(r_dict, "vertex_shader", item = PyUnicode_FromString(vert)); Py_DECREF(item); PyDict_SetItemString(r_dict, "fragment_shader", item = PyUnicode_FromString(frag)); Py_DECREF(item); if (geom) { PyDict_SetItemString(r_dict, "geometry_shader", item = PyUnicode_FromString(geom)); Py_DECREF(item); } if (defines) { PyDict_SetItemString(r_dict, "defines", item = PyUnicode_FromString(defines)); Py_DECREF(item); } return r_dict; } static struct PyMethodDef bpygpu_shader_module_methods[] = { {"unbind", (PyCFunction)bpygpu_shader_unbind, METH_NOARGS, bpygpu_shader_unbind_doc}, {"shader_from_builtin", (PyCFunction)bpygpu_shader_from_builtin, METH_O, bpygpu_shader_from_builtin_doc}, {"shader_code_from_builtin", (PyCFunction)bpygpu_shader_code_from_builtin, METH_O, bpygpu_shader_code_from_builtin_doc}, {NULL, NULL, 0, NULL} }; PyDoc_STRVAR(bpygpu_shader_module_doc, "This module provides access to GPUShader internal functions." ); static PyModuleDef BPyGPU_shader_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.shader", .m_doc = bpygpu_shader_module_doc, .m_methods = bpygpu_shader_module_methods, }; /** \} */ /* -------------------------------------------------------------------- */ /** \name gpu.shader.buitin Module API * \{ */ PyDoc_STRVAR(bpygpu_shader_builtin_module_doc, "This module contains integers that identify the built-in shader ids." ); static PyModuleDef BPyGPU_shader_builtin_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.shader.builtin", .m_doc = bpygpu_shader_builtin_module_doc, }; /** \} */ /* -------------------------------------------------------------------- */ /** \name Public API * \{ */ PyObject *BPyGPUShader_CreatePyObject(GPUShader *shader) { BPyGPUShader *self; self = PyObject_New(BPyGPUShader, &BPyGPUShader_Type); self->shader = shader; return (PyObject *)self; } PyObject *BPyInit_gpu_shader(void) { PyObject *submodule; submodule = PyModule_Create(&BPyGPU_shader_module_def); return submodule; } PyObject *BPyInit_gpu_shader_builtin(void) { PyObject *submodule; submodule = PyModule_Create(&BPyGPU_shader_builtin_module_def); bpygpu_shader_add_enum_objects(submodule); return submodule; } /** \} */