diff options
Diffstat (limited to 'source/blender/python/intern')
-rw-r--r-- | source/blender/python/intern/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/python/intern/gpu.c | 19 | ||||
-rw-r--r-- | source/blender/python/intern/gpu.h | 2 | ||||
-rw-r--r-- | source/blender/python/intern/gpu_offscreen.c | 424 |
4 files changed, 443 insertions, 3 deletions
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index cd2a9fd8584..f04bca75a8c 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -46,6 +46,7 @@ set(INC_SYS set(SRC gpu.c + gpu_offscreen.c bpy.c bpy_app.c bpy_app_build_options.c diff --git a/source/blender/python/intern/gpu.c b/source/blender/python/intern/gpu.c index 65b2e97f461..aada3f6fc80 100644 --- a/source/blender/python/intern/gpu.c +++ b/source/blender/python/intern/gpu.c @@ -55,7 +55,7 @@ #define PY_MODULE_ADD_CONSTANT(module, name) PyModule_AddIntConstant(module, # name, name) PyDoc_STRVAR(M_gpu_doc, -"This module provides access to the GLSL shader." +"This module provides access to the GLSL shader and Offscreen rendering functionalities." ); static struct PyModuleDef gpumodule = { PyModuleDef_HEAD_INIT, @@ -309,12 +309,25 @@ static PyMethodDef meth_export_shader[] = { {"export_shader", (PyCFunction)GPU_export_shader, METH_VARARGS | METH_KEYWORDS, GPU_export_shader_doc} }; +/* -------------------------------------------------------------------- */ +/* Initialize Module */ + PyObject *GPU_initPython(void) { - PyObject *module = PyInit_gpu(); + PyObject *module; + PyObject *submodule; + PyObject *sys_modules = PyThreadState_GET()->interp->modules; + + module = PyInit_gpu(); + PyModule_AddObject(module, "export_shader", (PyObject *)PyCFunction_New(meth_export_shader, NULL)); - PyDict_SetItemString(PyImport_GetModuleDict(), "gpu", module); + /* gpu.offscreen */ + PyModule_AddObject(module, "offscreen", (submodule = BPyInit_gpu_offscreen())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + PyDict_SetItemString(PyImport_GetModuleDict(), "gpu", module); return module; } diff --git a/source/blender/python/intern/gpu.h b/source/blender/python/intern/gpu.h index 82338869b9d..0da44a4eb87 100644 --- a/source/blender/python/intern/gpu.h +++ b/source/blender/python/intern/gpu.h @@ -36,4 +36,6 @@ PyObject *GPU_initPython(void); +PyObject *BPyInit_gpu_offscreen(void); + #endif /* __GPU_H__ */ diff --git a/source/blender/python/intern/gpu_offscreen.c b/source/blender/python/intern/gpu_offscreen.c new file mode 100644 index 00000000000..f8285c6139f --- /dev/null +++ b/source/blender/python/intern/gpu_offscreen.c @@ -0,0 +1,424 @@ +/* + * ***** 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. + * + * Copyright 2015, Blender Foundation. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/gpu_offscreen.c + * \ingroup pythonintern + * + * This file defines the offscreen functionalities of the 'gpu' module + * used for off-screen OpenGL rendering. + */ + +#include <Python.h> + +#include "BLI_utildefines.h" + +#include "WM_types.h" + +#include "ED_screen.h" + +#include "GPU_extensions.h" +#include "GPU_compositing.h" + +#include "../mathutils/mathutils.h" + +#include "../generic/py_capi_utils.h" + +#include "gpu.h" + +#include "ED_view3d.h" + +/* -------------------------------------------------------------------- */ +/* GPU Offscreen PyObject */ + +typedef struct { + PyObject_HEAD + GPUOffScreen *ofs; +} BPy_GPUOffScreen; + +static int bpy_gpu_offscreen_valid_check(BPy_GPUOffScreen *py_gpu_ofs) +{ + if (UNLIKELY(py_gpu_ofs->ofs == NULL)) { + PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid"); + return -1; + } + return 0; +} + +#define BPY_GPU_OFFSCREEN_CHECK_OBJ(pygpu) { \ + if (UNLIKELY(bpy_gpu_offscreen_valid_check(pygpu) == -1)) { \ + return NULL; \ + } \ +} ((void)0) + +PyDoc_STRVAR(pygpu_offscreen_width_doc, "Texture width.\n\n:type: int"); +static PyObject *pygpu_offscreen_width_get(BPy_GPUOffScreen *self, void *UNUSED(type)) +{ + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + return PyLong_FromLong(GPU_offscreen_width(self->ofs)); +} + +PyDoc_STRVAR(pygpu_offscreen_height_doc, "Texture height.\n\n:type: int"); +static PyObject *pygpu_offscreen_height_get(BPy_GPUOffScreen *self, void *UNUSED(type)) +{ + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + return PyLong_FromLong(GPU_offscreen_height(self->ofs)); +} + +PyDoc_STRVAR(pygpu_offscreen_color_texture_doc, "Color texture.\n\n:type: int"); +static PyObject *pygpu_offscreen_color_texture_get(BPy_GPUOffScreen *self, void *UNUSED(type)) +{ + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + return PyLong_FromLong(GPU_offscreen_color_texture(self->ofs)); +} + +PyDoc_STRVAR(pygpu_offscreen_bind_doc, +"bind(save=True)\n" +"\n" +" Bind the offscreen object.\n" +"\n" +" :param save: save OpenGL current states.\n" +" :type save: bool\n" +); +static PyObject *pygpu_offscreen_bind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds) +{ + static const char *kwlist[] = {"save", NULL}; + bool save = true; + + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|O&:bind", (char **)(kwlist), + PyC_ParseBool, &save)) + { + return NULL; + } + + GPU_offscreen_bind(self->ofs, save); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_offscreen_unbind_doc, +"unbind(restore=True)\n" +"\n" +" Unbind the offscreen object.\n" +"\n" +" :param restore: restore OpenGL previous states.\n" +" :type restore: bool\n" +); +static PyObject *pygpu_offscreen_unbind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds) +{ + static const char *kwlist[] = {"restore", NULL}; + bool restore = true; + + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|O&:unbind", (char **)(kwlist), + PyC_ParseBool, &restore)) + { + return NULL; + } + + GPU_offscreen_unbind(self->ofs, restore); + Py_RETURN_NONE; +} + +/** + * Use with PyArg_ParseTuple's "O&" formatting. + */ +static int pygpu_offscreen_check_matrix(PyObject *o, void *p) +{ + MatrixObject **pymat_p = p; + MatrixObject *pymat = (MatrixObject *)o; + + if (!MatrixObject_Check(pymat)) { + PyErr_Format(PyExc_TypeError, + "expected a mathutils.Matrix, not a %.200s", + Py_TYPE(o)->tp_name); + return 0; + } + + if (BaseMath_ReadCallback(pymat) == -1) { + return 0; + } + + if ((pymat->num_col != 4) || + (pymat->num_row != 4)) + { + PyErr_SetString(PyExc_ValueError, "matrix must be 4x4"); + return 0; + } + + *pymat_p = pymat; + return 1; +} + +PyDoc_STRVAR(pygpu_offscreen_draw_view3d_doc, +"draw_view3d(scene, view3d, region, modelview_matrix, projection_matrix)\n" +"\n" +" Draw the 3d viewport in the offscreen object.\n" +"\n" +" :param scene: Scene to draw.\n" +" :type scene: :class:`bpy.types.Scene`\n" +" :param view3d: 3D View to get the drawing settings from.\n" +" :type view3d: :class:`bpy.types.SpaceView3D`\n" +" :param region: Region of the 3D View.\n" +" :type region: :class:`bpy.types.Region`\n" +" :param modelview_matrix: ModelView Matrix.\n" +" :type modelview_matrix: :class:`mathutils.Matrix`\n" +" :param projection_matrix: Projection Matrix.\n" +" :type projection_matrix: :class:`mathutils.Matrix`\n" +); +static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds) +{ + static const char *kwlist[] = {"scene", "view3d", "region", "projection_matrix", "modelview_matrix", NULL}; + + MatrixObject *py_mat_modelview, *py_mat_projection; + PyObject *py_scene, *py_region, *py_view3d; + + Scene *scene; + View3D *v3d; + ARegion *ar; + GPUFX *fx; + GPUFXSettings fx_settings; + + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "OOOO&O&:draw_view3d", (char **)(kwlist), + &py_scene, &py_view3d, &py_region, + pygpu_offscreen_check_matrix, &py_mat_projection, + pygpu_offscreen_check_matrix, &py_mat_modelview) || + (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) || + !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) || + !(ar = PyC_RNA_AsPointer(py_region, "Region")))) + { + return NULL; + } + + fx = GPU_fx_compositor_create(); + + fx_settings = v3d->fx_settings; /* full copy */ + + ED_view3d_draw_offscreen_init(scene, v3d); + + GPU_offscreen_bind(self->ofs, true); /* bind */ + + ED_view3d_draw_offscreen( + scene, v3d, ar, GPU_offscreen_width(self->ofs), GPU_offscreen_height(self->ofs), + (float(*)[4])py_mat_modelview->matrix, (float(*)[4])py_mat_projection->matrix, + false, true, true, "", + fx, &fx_settings, + self->ofs); + + GPU_fx_compositor_destroy(fx); + GPU_offscreen_unbind(self->ofs, true); /* unbind */ + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_offscreen_free_doc, +"free()\n" +"\n" +" Free the offscreen object\n" +" The framebuffer, texture and render objects will no longer be accessible.\n" +); +static PyObject *pygpu_offscreen_free(BPy_GPUOffScreen *self) +{ + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + + GPU_offscreen_free(self->ofs); + self->ofs = NULL; + Py_RETURN_NONE; +} + +static void BPy_GPUOffScreen__tp_dealloc(BPy_GPUOffScreen *self) +{ + if (self->ofs) + GPU_offscreen_free(self->ofs); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyGetSetDef bpy_gpu_offscreen_getseters[] = { + {(char *)"color_texture", (getter)pygpu_offscreen_color_texture_get, (setter)NULL, pygpu_offscreen_color_texture_doc, NULL}, + {(char *)"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL}, + {(char *)"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL}, + {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ +}; + +static struct PyMethodDef bpy_gpu_offscreen_methods[] = { + {"bind", (PyCFunction)pygpu_offscreen_bind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_bind_doc}, + {"unbind", (PyCFunction)pygpu_offscreen_unbind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_unbind_doc}, + {"draw_view3d", (PyCFunction)pygpu_offscreen_draw_view3d, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_draw_view3d_doc}, + {"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc}, + {NULL, NULL, 0, NULL} +}; + +PyDoc_STRVAR(py_gpu_offscreen_doc, +".. class:: GPUOffscreen" +"\n" +" This object gives access to off screen buffers.\n" +); +static PyTypeObject BPy_GPUOffScreen_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "GPUOffScreen", /* tp_name */ + sizeof(BPy_GPUOffScreen), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)BPy_GPUOffScreen__tp_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + NULL, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL, /* tp_hash */ + NULL, /* tp_call */ + NULL, /* tp_str */ + NULL, /* tp_getattro */ + NULL, /* tp_setattro */ + NULL, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + py_gpu_offscreen_doc, /* Documentation string */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + bpy_gpu_offscreen_methods, /* tp_methods */ + NULL, /* tp_members */ + bpy_gpu_offscreen_getseters, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + NULL, /* tp_alloc */ + NULL, /* tp_new */ + (freefunc)0, /* tp_free */ + NULL, /* tp_is_gc */ + NULL, /* tp_bases */ + NULL, /* tp_mro */ + NULL, /* tp_cache */ + NULL, /* tp_subclasses */ + NULL, /* tp_weaklist */ + (destructor) NULL /* tp_del */ +}; + +/* -------------------------------------------------------------------- */ +/* GPU offscreen methods */ + +static PyObject *BPy_GPU_OffScreen_CreatePyObject(GPUOffScreen *ofs) +{ + BPy_GPUOffScreen *self; + self = PyObject_New(BPy_GPUOffScreen, &BPy_GPUOffScreen_Type); + self->ofs = ofs; + return (PyObject *)self; +} + +PyDoc_STRVAR(pygpu_offscreen_new_doc, +"new(width, height, samples=0)\n" +"\n" +" Return a GPUOffScreen.\n" +"\n" +" :param width: Horizontal dimension of the buffer.\n" +" :type width: int`\n" +" :param height: Vertical dimension of the buffer.\n" +" :type height: int`\n" +" :param samples: OpenGL samples to use for MSAA or zero to disable.\n" +" :type samples: int\n" +" :return: Newly created off-screen buffer.\n" +" :rtype: :class:`gpu.GPUOffscreen`\n" +); +static PyObject *pygpu_offscreen_new(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) +{ + static const char *kwlist[] = {"width", "height", "samples", NULL}; + + GPUOffScreen *ofs; + int width, height, samples = 0; + char err_out[256]; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "ii|i:new", (char **)(kwlist), + &width, &height, &samples)) + { + return NULL; + } + + ofs = GPU_offscreen_create(width, height, samples, err_out); + + if (ofs == NULL) { + PyErr_Format(PyExc_RuntimeError, + "gpu.offscreen.new(...) failed with '%s'", + err_out[0] ? err_out : "unknown error"); + return NULL; + } + + return BPy_GPU_OffScreen_CreatePyObject(ofs); +} + +static struct PyMethodDef BPy_GPU_offscreen_methods[] = { + {"new", (PyCFunction)pygpu_offscreen_new, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_new_doc}, + {NULL, NULL, 0, NULL} +}; + +PyDoc_STRVAR(BPy_GPU_offscreen_doc, +"This module provides access to offscreen rendering functions." +); +static PyModuleDef BPy_GPU_offscreen_module_def = { + PyModuleDef_HEAD_INIT, + "gpu.offscreen", /* m_name */ + BPy_GPU_offscreen_doc, /* m_doc */ + 0, /* m_size */ + BPy_GPU_offscreen_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + +PyObject *BPyInit_gpu_offscreen(void) +{ + PyObject *submodule; + + /* Register the 'GPUOffscreen' class */ + if (PyType_Ready(&BPy_GPUOffScreen_Type)) { + return NULL; + } + + submodule = PyModule_Create(&BPy_GPU_offscreen_module_def); + +#define MODULE_TYPE_ADD(s, t) \ + PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t) + + MODULE_TYPE_ADD(submodule, BPy_GPUOffScreen_Type); + +#undef MODULE_TYPE_ADD + + return submodule; +} + +#undef BPY_GPU_OFFSCREEN_CHECK_OBJ |