gpu.offscreen, fix pydoc, example and rename modelviewmatrix > viewmatrix
[blender.git] / source / blender / python / gpu / gpu_py_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/gpu/gpu_py_offscreen.c
24  *  \ingroup bpygpu
25  *
26  * This file defines the offscreen functionalities of the 'gpu' module
27  * used for off-screen OpenGL rendering.
28  *
29  * - Use ``bpygpu_`` for local API.
30  * - Use ``BPyGPU`` for public API.
31  */
32
33 #include <Python.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_utildefines.h"
38
39 #include "BKE_global.h"
40 #include "BKE_library.h"
41 #include "BKE_scene.h"
42
43 #include "DNA_screen_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_view3d_types.h"
46
47 #include "GPU_framebuffer.h"
48 #include "GPU_texture.h"
49
50 #include "../editors/include/ED_view3d.h"
51
52 #include "../mathutils/mathutils.h"
53
54 #include "../generic/py_capi_utils.h"
55
56 #include "gpu_py_offscreen.h" /* own include */
57
58 /* -------------------------------------------------------------------- */
59
60 /** \name GPUOffscreen Type
61  * \{ */
62
63 static PyObject *bpygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds)
64 {
65         GPUOffScreen *ofs;
66         int width, height, samples = 0;
67         char err_out[256];
68
69         static const char *_keywords[] = {"width", "height", "samples", NULL};
70         static _PyArg_Parser _parser = {"ii|i:GPUOffScreen.__new__", _keywords, 0};
71         if (!_PyArg_ParseTupleAndKeywordsFast(
72                 args, kwds, &_parser,
73                 &width, &height, &samples))
74         {
75                 return NULL;
76         }
77
78         ofs = GPU_offscreen_create(width, height, samples, true, false, err_out);
79
80         if (ofs == NULL) {
81                 PyErr_Format(PyExc_RuntimeError,
82                              "gpu.offscreen.new(...) failed with '%s'",
83                              err_out[0] ? err_out : "unknown error");
84                 return NULL;
85         }
86
87         return BPyGPUOffScreen_CreatePyObject(ofs);
88 }
89
90 static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs)
91 {
92         if (UNLIKELY(bpygpu_ofs->ofs == NULL)) {
93                 PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid");
94                 return -1;
95         }
96         return 0;
97 }
98
99 #define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) { \
100         if (UNLIKELY(bpygpu_offscreen_valid_check(bpygpu) == -1)) { \
101                 return NULL; \
102         } \
103 } ((void)0)
104
105 PyDoc_STRVAR(bpygpu_offscreen_width_doc, "Texture width.\n\n:type: int");
106 static PyObject *bpygpu_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type))
107 {
108         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
109         return PyLong_FromLong(GPU_offscreen_width(self->ofs));
110 }
111
112 PyDoc_STRVAR(bpygpu_offscreen_height_doc, "Texture height.\n\n:type: int");
113 static PyObject *bpygpu_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type))
114 {
115         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
116         return PyLong_FromLong(GPU_offscreen_height(self->ofs));
117 }
118
119 PyDoc_STRVAR(bpygpu_offscreen_color_texture_doc, "Color texture.\n\n:type: int");
120 static PyObject *bpygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *UNUSED(type))
121 {
122         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
123         GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
124         return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
125 }
126
127 PyDoc_STRVAR(bpygpu_offscreen_bind_doc,
128 "bind(save=True)\n"
129 "\n"
130 "   Bind the offscreen object.\n"
131 "\n"
132 "   :param save: save OpenGL current states.\n"
133 "   :type save: bool\n"
134 );
135 static PyObject *bpygpu_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
136 {
137         bool save = true;
138
139         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
140
141         static const char *_keywords[] = {"save", NULL};
142         static _PyArg_Parser _parser = {"|O&:bind", _keywords, 0};
143         if (!_PyArg_ParseTupleAndKeywordsFast(
144                 args, kwds, &_parser,
145                 PyC_ParseBool, &save))
146         {
147                 return NULL;
148         }
149
150         GPU_offscreen_bind(self->ofs, save);
151         Py_RETURN_NONE;
152 }
153
154 PyDoc_STRVAR(bpygpu_offscreen_unbind_doc,
155 "unbind(restore=True)\n"
156 "\n"
157 "   Unbind the offscreen object.\n"
158 "\n"
159 "   :param restore: restore OpenGL previous states.\n"
160 "   :type restore: bool\n"
161 );
162 static PyObject *bpygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
163 {
164         bool restore = true;
165
166         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
167
168         static const char *_keywords[] = {"restore", NULL};
169         static _PyArg_Parser _parser = {"|O&:unbind", _keywords, 0};
170         if (!_PyArg_ParseTupleAndKeywordsFast(
171                 args, kwds, &_parser,
172                 PyC_ParseBool, &restore))
173         {
174                 return NULL;
175         }
176
177         GPU_offscreen_unbind(self->ofs, restore);
178         Py_RETURN_NONE;
179 }
180
181 PyDoc_STRVAR(bpygpu_offscreen_draw_view3d_doc,
182 "draw_view3d(scene, view3d, region, modelview_matrix, projection_matrix)\n"
183 "\n"
184 "   Draw the 3d viewport in the offscreen object.\n"
185 "\n"
186 "   :param scene: Scene to draw.\n"
187 "   :type scene: :class:`bpy.types.Scene`\n"
188 "   :param view_layer: View layer to draw.\n"
189 "   :type view_layer: :class:`bpy.types.ViewLayer`\n"
190 "   :param view3d: 3D View to get the drawing settings from.\n"
191 "   :type view3d: :class:`bpy.types.SpaceView3D`\n"
192 "   :param region: Region of the 3D View.\n"
193 "   :type region: :class:`bpy.types.Region`\n"
194 "   :param view_matrix: View Matrix.\n"
195 "   :type view_matrix: :class:`mathutils.Matrix`\n"
196 "   :param projection_matrix: Projection Matrix.\n"
197 "   :type projection_matrix: :class:`mathutils.Matrix`\n"
198 );
199 static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
200 {
201         MatrixObject *py_mat_view, *py_mat_projection;
202         PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
203
204         struct Depsgraph *depsgraph;
205         struct Scene *scene;
206         struct ViewLayer *view_layer;
207         View3D *v3d;
208         ARegion *ar;
209         struct RV3DMatrixStore *rv3d_mats;
210
211         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
212
213         static const char *_keywords[] = {
214                 "scene", "view_layer", "view3d", "region",
215                 "projection_matrix", "view_matrix", NULL};
216
217         static _PyArg_Parser _parser = {"OOOOO&O&:draw_view3d", _keywords, 0};
218         if (!_PyArg_ParseTupleAndKeywordsFast(
219                 args, kwds, &_parser,
220                 &py_scene, &py_view_layer, &py_view3d, &py_region,
221                 Matrix_Parse4x4, &py_mat_projection,
222                 Matrix_Parse4x4, &py_mat_view) ||
223             (!(scene       = PyC_RNA_AsPointer(py_scene, "Scene")) ||
224              !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) ||
225              !(v3d         = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
226              !(ar          = PyC_RNA_AsPointer(py_region, "Region"))))
227         {
228                 return NULL;
229         }
230
231         BLI_assert(BKE_id_is_in_global_main(&scene->id));
232
233         depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
234
235         rv3d_mats = ED_view3d_mats_rv3d_backup(ar->regiondata);
236
237         GPU_offscreen_bind(self->ofs, true); /* bind */
238
239         ED_view3d_draw_offscreen(depsgraph,
240                                  scene,
241                                  v3d->shading.type,
242                                  v3d,
243                                  ar,
244                                  GPU_offscreen_width(self->ofs),
245                                  GPU_offscreen_height(self->ofs),
246                                  (float(*)[4])py_mat_view->matrix,
247                                  (float(*)[4])py_mat_projection->matrix,
248                                  false,
249                                  true,
250                                  "",
251                                  NULL,
252                                  self->ofs,
253                                  NULL);
254
255         GPU_offscreen_unbind(self->ofs, true); /* unbind */
256
257         ED_view3d_mats_rv3d_restore(ar->regiondata, rv3d_mats);
258         MEM_freeN(rv3d_mats);
259
260         Py_RETURN_NONE;
261 }
262
263 PyDoc_STRVAR(bpygpu_offscreen_free_doc,
264 "free()\n"
265 "\n"
266 "   Free the offscreen object\n"
267 "   The framebuffer, texture and render objects will no longer be accessible.\n"
268 );
269 static PyObject *bpygpu_offscreen_free(BPyGPUOffScreen *self)
270 {
271         BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
272
273         GPU_offscreen_free(self->ofs);
274         self->ofs = NULL;
275         Py_RETURN_NONE;
276 }
277
278 static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
279 {
280         if (self->ofs)
281                 GPU_offscreen_free(self->ofs);
282         Py_TYPE(self)->tp_free((PyObject *)self);
283 }
284
285 static PyGetSetDef bpygpu_offscreen_getseters[] = {
286         {(char *)"color_texture", (getter)bpygpu_offscreen_color_texture_get, (setter)NULL, bpygpu_offscreen_color_texture_doc, NULL},
287         {(char *)"width", (getter)bpygpu_offscreen_width_get, (setter)NULL, bpygpu_offscreen_width_doc, NULL},
288         {(char *)"height", (getter)bpygpu_offscreen_height_get, (setter)NULL, bpygpu_offscreen_height_doc, NULL},
289         {NULL, NULL, NULL, NULL, NULL}  /* Sentinel */
290 };
291
292 static struct PyMethodDef bpygpu_offscreen_methods[] = {
293         {"bind", (PyCFunction)bpygpu_offscreen_bind, METH_VARARGS | METH_KEYWORDS, bpygpu_offscreen_bind_doc},
294         {"unbind", (PyCFunction)bpygpu_offscreen_unbind, METH_VARARGS | METH_KEYWORDS, bpygpu_offscreen_unbind_doc},
295         {"draw_view3d", (PyCFunction)bpygpu_offscreen_draw_view3d, METH_VARARGS | METH_KEYWORDS, bpygpu_offscreen_draw_view3d_doc},
296         {"free", (PyCFunction)bpygpu_offscreen_free, METH_NOARGS, bpygpu_offscreen_free_doc},
297         {NULL, NULL, 0, NULL}
298 };
299
300 PyDoc_STRVAR(bpygpu_offscreen_doc,
301 "GPUOffScreen(width, height, samples=0)\n"
302 "\n"
303 "   This object gives access to off screen buffers.\n"
304 "\n"
305 "   :param width: Horizontal dimension of the buffer.\n"
306 "   :type width: `int`\n"
307 "   :param height: Vertical dimension of the buffer.\n"
308 "   :type height: `int`\n"
309 "   :param samples: OpenGL samples to use for MSAA or zero to disable.\n"
310 "   :type samples: `int`\n"
311 );
312 PyTypeObject BPyGPUOffScreen_Type = {
313         PyVarObject_HEAD_INIT(NULL, 0)
314         .tp_name = "GPUOffScreen",
315         .tp_basicsize = sizeof(BPyGPUOffScreen),
316         .tp_dealloc = (destructor)BPyGPUOffScreen__tp_dealloc,
317         .tp_flags = Py_TPFLAGS_DEFAULT,
318         .tp_doc = bpygpu_offscreen_doc,
319         .tp_methods = bpygpu_offscreen_methods,
320         .tp_getset = bpygpu_offscreen_getseters,
321         .tp_new = bpygpu_offscreen_new,
322 };
323
324 /** \} */
325
326
327 /* -------------------------------------------------------------------- */
328
329 /** \name Public API
330  * \{ */
331
332 PyObject *BPyGPUOffScreen_CreatePyObject(GPUOffScreen *ofs)
333 {
334         BPyGPUOffScreen *self;
335
336         self = PyObject_New(BPyGPUOffScreen, &BPyGPUOffScreen_Type);
337         self->ofs = ofs;
338
339         return (PyObject *)self;
340 }
341
342 /** \} */
343
344 #undef BPY_GPU_OFFSCREEN_CHECK_OBJ