Cleanup: get rid of last G.main's in bpy area.
[blender.git] / source / blender / python / intern / gpu_offscreen.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Copyright 2015, Blender Foundation.
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/python/intern/gpu_offscreen.c
24  *  \ingroup pythonintern
25  *
26  * This file defines the offscreen functionalities of the 'gpu' module
27  * used for off-screen OpenGL rendering.
28  */
29
30 #include <Python.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_utildefines.h"
35
36 #include "WM_types.h"
37
38 #include "BKE_global.h"
39 #include "BKE_library.h"
40
41 #include "ED_screen.h"
42
43 #include "GPU_compositing.h"
44 #include "GPU_framebuffer.h"
45
46 #include "../mathutils/mathutils.h"
47
48 #include "../generic/py_capi_utils.h"
49
50 #include "gpu.h"
51
52 #include "ED_view3d.h"
53
54 /* -------------------------------------------------------------------- */
55 /* GPU Offscreen PyObject */
56
57 typedef struct {
58         PyObject_HEAD
59         GPUOffScreen *ofs;
60 } BPy_GPUOffScreen;
61
62 static int bpy_gpu_offscreen_valid_check(BPy_GPUOffScreen *py_gpu_ofs)
63 {
64         if (UNLIKELY(py_gpu_ofs->ofs == NULL)) {
65                 PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid");
66                 return -1;
67         }
68         return 0;
69 }
70
71 #define BPY_GPU_OFFSCREEN_CHECK_OBJ(pygpu) { \
72         if (UNLIKELY(bpy_gpu_offscreen_valid_check(pygpu) == -1)) { \
73                 return NULL; \
74         } \
75 } ((void)0)
76
77 PyDoc_STRVAR(pygpu_offscreen_width_doc, "Texture width.\n\n:type: int");
78 static PyObject *pygpu_offscreen_width_get(BPy_GPUOffScreen *self, void *UNUSED(type))
79 {
80         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
81         return PyLong_FromLong(GPU_offscreen_width(self->ofs));
82 }
83
84 PyDoc_STRVAR(pygpu_offscreen_height_doc, "Texture height.\n\n:type: int");
85 static PyObject *pygpu_offscreen_height_get(BPy_GPUOffScreen *self, void *UNUSED(type))
86 {
87         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
88         return PyLong_FromLong(GPU_offscreen_height(self->ofs));
89 }
90
91 PyDoc_STRVAR(pygpu_offscreen_color_texture_doc, "Color texture.\n\n:type: int");
92 static PyObject *pygpu_offscreen_color_texture_get(BPy_GPUOffScreen *self, void *UNUSED(type))
93 {
94         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
95         return PyLong_FromLong(GPU_offscreen_color_texture(self->ofs));
96 }
97
98 PyDoc_STRVAR(pygpu_offscreen_bind_doc,
99 "bind(save=True)\n"
100 "\n"
101 "   Bind the offscreen object.\n"
102 "\n"
103 "   :param save: save OpenGL current states.\n"
104 "   :type save: bool\n"
105 );
106 static PyObject *pygpu_offscreen_bind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
107 {
108         static const char *kwlist[] = {"save", NULL};
109         bool save = true;
110
111         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
112
113         if (!PyArg_ParseTupleAndKeywords(
114                 args, kwds, "|O&:bind", (char **)(kwlist),
115                 PyC_ParseBool, &save))
116         {
117                 return NULL;
118         }
119
120         GPU_offscreen_bind(self->ofs, save);
121         Py_RETURN_NONE;
122 }
123
124 PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
125 "unbind(restore=True)\n"
126 "\n"
127 "   Unbind the offscreen object.\n"
128 "\n"
129 "   :param restore: restore OpenGL previous states.\n"
130 "   :type restore: bool\n"
131 );
132 static PyObject *pygpu_offscreen_unbind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
133 {
134         static const char *kwlist[] = {"restore", NULL};
135         bool restore = true;
136
137         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
138
139         if (!PyArg_ParseTupleAndKeywords(
140                 args, kwds, "|O&:unbind", (char **)(kwlist),
141                 PyC_ParseBool, &restore))
142         {
143                 return NULL;
144         }
145
146         GPU_offscreen_unbind(self->ofs, restore);
147         Py_RETURN_NONE;
148 }
149
150 PyDoc_STRVAR(pygpu_offscreen_draw_view3d_doc,
151 "draw_view3d(scene, view3d, region, modelview_matrix, projection_matrix)\n"
152 "\n"
153 "   Draw the 3d viewport in the offscreen object.\n"
154 "\n"
155 "   :param scene: Scene to draw.\n"
156 "   :type scene: :class:`bpy.types.Scene`\n"
157 "   :param view3d: 3D View to get the drawing settings from.\n"
158 "   :type view3d: :class:`bpy.types.SpaceView3D`\n"
159 "   :param region: Region of the 3D View.\n"
160 "   :type region: :class:`bpy.types.Region`\n"
161 "   :param modelview_matrix: ModelView Matrix.\n"
162 "   :type modelview_matrix: :class:`mathutils.Matrix`\n"
163 "   :param projection_matrix: Projection Matrix.\n"
164 "   :type projection_matrix: :class:`mathutils.Matrix`\n"
165 );
166 static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
167 {
168         static const char *kwlist[] = {"scene", "view3d", "region", "projection_matrix", "modelview_matrix", NULL};
169
170         MatrixObject *py_mat_modelview, *py_mat_projection;
171         PyObject *py_scene, *py_region, *py_view3d;
172
173         struct Main *bmain = G_MAIN;  /* XXX UGLY! */
174         Scene *scene;
175         View3D *v3d;
176         ARegion *ar;
177         GPUFX *fx;
178         GPUFXSettings fx_settings;
179         struct RV3DMatrixStore *rv3d_mats;
180
181         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
182
183         if (!PyArg_ParseTupleAndKeywords(
184                 args, kwds, "OOOO&O&:draw_view3d", (char **)(kwlist),
185                 &py_scene, &py_view3d, &py_region,
186                 Matrix_Parse4x4, &py_mat_projection,
187                 Matrix_Parse4x4, &py_mat_modelview) ||
188             (!(scene    = PyC_RNA_AsPointer(py_scene, "Scene")) ||
189              !(v3d      = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
190              !(ar       = PyC_RNA_AsPointer(py_region, "Region"))))
191         {
192                 return NULL;
193         }
194
195         BLI_assert(BKE_id_is_in_gobal_main(&scene->id));
196
197         fx = GPU_fx_compositor_create();
198
199         fx_settings = v3d->fx_settings;  /* full copy */
200
201         ED_view3d_draw_offscreen_init(bmain, scene, v3d);
202
203         rv3d_mats = ED_view3d_mats_rv3d_backup(ar->regiondata);
204
205         GPU_offscreen_bind(self->ofs, true); /* bind */
206
207         ED_view3d_draw_offscreen(
208                 bmain, scene, v3d, ar, GPU_offscreen_width(self->ofs), GPU_offscreen_height(self->ofs),
209                 (float(*)[4])py_mat_modelview->matrix, (float(*)[4])py_mat_projection->matrix,
210                 false, true, true, "",
211                 fx, &fx_settings,
212                 self->ofs);
213
214         GPU_fx_compositor_destroy(fx);
215         GPU_offscreen_unbind(self->ofs, true); /* unbind */
216
217         ED_view3d_mats_rv3d_restore(ar->regiondata, rv3d_mats);
218         MEM_freeN(rv3d_mats);
219
220         Py_RETURN_NONE;
221 }
222
223 PyDoc_STRVAR(pygpu_offscreen_free_doc,
224 "free()\n"
225 "\n"
226 "   Free the offscreen object\n"
227 "   The framebuffer, texture and render objects will no longer be accessible.\n"
228 );
229 static PyObject *pygpu_offscreen_free(BPy_GPUOffScreen *self)
230 {
231         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
232
233         GPU_offscreen_free(self->ofs);
234         self->ofs = NULL;
235         Py_RETURN_NONE;
236 }
237
238 static void BPy_GPUOffScreen__tp_dealloc(BPy_GPUOffScreen *self)
239 {
240         if (self->ofs)
241                 GPU_offscreen_free(self->ofs);
242         Py_TYPE(self)->tp_free((PyObject *)self);
243 }
244
245 static PyGetSetDef bpy_gpu_offscreen_getseters[] = {
246         {(char *)"color_texture", (getter)pygpu_offscreen_color_texture_get, (setter)NULL, pygpu_offscreen_color_texture_doc, NULL},
247         {(char *)"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
248         {(char *)"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
249         {NULL, NULL, NULL, NULL, NULL}  /* Sentinel */
250 };
251
252 static struct PyMethodDef bpy_gpu_offscreen_methods[] = {
253         {"bind", (PyCFunction)pygpu_offscreen_bind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_bind_doc},
254         {"unbind", (PyCFunction)pygpu_offscreen_unbind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_unbind_doc},
255         {"draw_view3d", (PyCFunction)pygpu_offscreen_draw_view3d, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_draw_view3d_doc},
256         {"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc},
257         {NULL, NULL, 0, NULL}
258 };
259
260 PyDoc_STRVAR(py_gpu_offscreen_doc,
261 ".. class:: GPUOffscreen"
262 "\n"
263 "   This object gives access to off screen buffers.\n"
264 );
265 static PyTypeObject BPy_GPUOffScreen_Type = {
266         PyVarObject_HEAD_INIT(NULL, 0)
267         "GPUOffScreen",                              /* tp_name */
268         sizeof(BPy_GPUOffScreen),                      /* tp_basicsize */
269         0,                                           /* tp_itemsize */
270         /* methods */
271         (destructor)BPy_GPUOffScreen__tp_dealloc,      /* tp_dealloc */
272         NULL,                                        /* tp_print */
273         NULL,                                        /* tp_getattr */
274         NULL,                                        /* tp_setattr */
275         NULL,                                        /* tp_compare */
276         NULL,                                        /* tp_repr */
277         NULL,                                        /* tp_as_number */
278         NULL,                                        /* tp_as_sequence */
279         NULL,                                        /* tp_as_mapping */
280         NULL,                                        /* tp_hash */
281         NULL,                                        /* tp_call */
282         NULL,                                        /* tp_str */
283         NULL,                                        /* tp_getattro */
284         NULL,                                        /* tp_setattro */
285         NULL,                                        /* tp_as_buffer */
286         Py_TPFLAGS_DEFAULT,                          /* tp_flags */
287         py_gpu_offscreen_doc,                        /* Documentation string */
288         NULL,                                        /* tp_traverse */
289         NULL,                                        /* tp_clear */
290         NULL,                                        /* tp_richcompare */
291         0,                                           /* tp_weaklistoffset */
292         NULL,                                        /* tp_iter */
293         NULL,                                        /* tp_iternext */
294         bpy_gpu_offscreen_methods,                   /* tp_methods */
295         NULL,                                        /* tp_members */
296         bpy_gpu_offscreen_getseters,                 /* tp_getset */
297         NULL,                                        /* tp_base */
298         NULL,                                        /* tp_dict */
299         NULL,                                        /* tp_descr_get */
300         NULL,                                        /* tp_descr_set */
301         0,                                           /* tp_dictoffset */
302         0,                                           /* tp_init */
303         NULL,                                        /* tp_alloc */
304         NULL,                                        /* tp_new */
305         (freefunc)0,                                 /* tp_free */
306         NULL,                                        /* tp_is_gc */
307         NULL,                                        /* tp_bases */
308         NULL,                                        /* tp_mro */
309         NULL,                                        /* tp_cache */
310         NULL,                                        /* tp_subclasses */
311         NULL,                                        /* tp_weaklist */
312         (destructor) NULL                            /* tp_del */
313 };
314
315 /* -------------------------------------------------------------------- */
316 /* GPU offscreen methods */
317
318 static PyObject *BPy_GPU_OffScreen_CreatePyObject(GPUOffScreen *ofs)
319 {
320         BPy_GPUOffScreen *self;
321         self = PyObject_New(BPy_GPUOffScreen, &BPy_GPUOffScreen_Type);
322         self->ofs = ofs;
323         return (PyObject *)self;
324 }
325
326 PyDoc_STRVAR(pygpu_offscreen_new_doc,
327 "new(width, height, samples=0)\n"
328 "\n"
329 "   Return a GPUOffScreen.\n"
330 "\n"
331 "   :param width: Horizontal dimension of the buffer.\n"
332 "   :type width: int`\n"
333 "   :param height: Vertical dimension of the buffer.\n"
334 "   :type height: int`\n"
335 "   :param samples: OpenGL samples to use for MSAA or zero to disable.\n"
336 "   :type samples: int\n"
337 "   :return: Newly created off-screen buffer.\n"
338 "   :rtype: :class:`gpu.GPUOffscreen`\n"
339 );
340 static PyObject *pygpu_offscreen_new(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
341 {
342         static const char *kwlist[] = {"width", "height", "samples", NULL};
343
344         GPUOffScreen *ofs;
345         int width, height, samples = 0;
346         char err_out[256];
347
348         if (!PyArg_ParseTupleAndKeywords(
349                 args, kwds, "ii|i:new", (char **)(kwlist),
350                 &width, &height, &samples))
351         {
352                 return NULL;
353         }
354
355         ofs = GPU_offscreen_create(width, height, samples, err_out);
356
357         if (ofs == NULL) {
358                 PyErr_Format(PyExc_RuntimeError,
359                              "gpu.offscreen.new(...) failed with '%s'",
360                              err_out[0] ? err_out : "unknown error");
361                 return NULL;
362         }
363
364         return BPy_GPU_OffScreen_CreatePyObject(ofs);
365 }
366
367 static struct PyMethodDef BPy_GPU_offscreen_methods[] = {
368         {"new", (PyCFunction)pygpu_offscreen_new, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_new_doc},
369         {NULL, NULL, 0, NULL}
370 };
371
372 PyDoc_STRVAR(BPy_GPU_offscreen_doc,
373 "This module provides access to offscreen rendering functions."
374 );
375 static PyModuleDef BPy_GPU_offscreen_module_def = {
376         PyModuleDef_HEAD_INIT,
377         "gpu.offscreen",                             /* m_name */
378         BPy_GPU_offscreen_doc,                       /* m_doc */
379         0,                                           /* m_size */
380         BPy_GPU_offscreen_methods,                   /* m_methods */
381         NULL,                                        /* m_reload */
382         NULL,                                        /* m_traverse */
383         NULL,                                        /* m_clear */
384         NULL,                                        /* m_free */
385 };
386
387 PyObject *BPyInit_gpu_offscreen(void)
388 {
389         PyObject *submodule;
390
391         /* Register the 'GPUOffscreen' class */
392         if (PyType_Ready(&BPy_GPUOffScreen_Type)) {
393                 return NULL;
394         }
395
396         submodule = PyModule_Create(&BPy_GPU_offscreen_module_def);
397
398 #define MODULE_TYPE_ADD(s, t) \
399         PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t)
400
401         MODULE_TYPE_ADD(submodule, BPy_GPUOffScreen_Type);
402
403 #undef MODULE_TYPE_ADD
404
405         return submodule;
406 }
407
408 #undef BPY_GPU_OFFSCREEN_CHECK_OBJ