2ab6fd864eb16148bd2ba653d63d503f1d4ae21f
[blender.git] / source / blender / python / intern / gpu_py_matrix.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/python/intern/gpu_py_matrix.c
22  *  \ingroup pythonintern
23  *
24  * This file defines the gpu.matrix stack API.
25  *
26  * \warning While these functions attempt to ensure correct stack usage.
27  * Mixing Python and C functions may still crash on invalid use.
28  */
29
30 #include <Python.h>
31
32
33 #include "BLI_utildefines.h"
34
35 #include "../mathutils/mathutils.h"
36
37 #include "../generic/py_capi_utils.h"
38
39 #include "gpu.h"
40
41 #define USE_GPU_PY_MATRIX_API
42 #include "GPU_matrix.h"
43 #undef USE_GPU_PY_MATRIX_API
44
45 /* -------------------------------------------------------------------- */
46 /** \name Helper Functions
47  * \{ */
48
49 static bool pygpu_stack_is_push_model_view_ok_or_error(void)
50 {
51         if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
52                 PyErr_SetString(PyExc_RuntimeError,
53                                 "Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
54                 return false;
55         }
56         return true;
57 }
58
59 static bool pygpu_stack_is_push_projection_ok_or_error(void)
60 {
61         if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
62                 PyErr_SetString(PyExc_RuntimeError,
63                                 "Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
64                 return false;
65         }
66         return true;
67 }
68
69 static bool pygpu_stack_is_pop_model_view_ok_or_error(void)
70 {
71         if (GPU_matrix_stack_level_get_model_view() == 0) {
72                 PyErr_SetString(PyExc_RuntimeError,
73                                 "Minimum model-view stack depth reached");
74                 return false;
75         }
76         return true;
77 }
78
79 static bool pygpu_stack_is_pop_projection_ok_or_error(void)
80 {
81         if (GPU_matrix_stack_level_get_projection() == 0) {
82                 PyErr_SetString(PyExc_RuntimeError,
83                                 "Minimum projection stack depth reached");
84                 return false;
85         }
86         return true;
87 }
88
89 /** \} */
90
91 /* -------------------------------------------------------------------- */
92 /** \name Manage Stack
93  * \{ */
94
95 PyDoc_STRVAR(pygpu_matrix_push_doc,
96 "push()\n"
97 "\n"
98 "   Add to the model-view matrix stack.\n"
99 );
100 static PyObject *pygpu_matrix_push(PyObject *UNUSED(self))
101 {
102         if (!pygpu_stack_is_push_model_view_ok_or_error()) {
103                 return NULL;
104         }
105         GPU_matrix_push();
106         Py_RETURN_NONE;
107 }
108
109 PyDoc_STRVAR(pygpu_matrix_pop_doc,
110 "pop()\n"
111 "\n"
112 "   Remove the last model-view matrix from the stack.\n"
113 );
114 static PyObject *pygpu_matrix_pop(PyObject *UNUSED(self))
115 {
116         if (!pygpu_stack_is_pop_model_view_ok_or_error()) {
117                 return NULL;
118         }
119         GPU_matrix_pop();
120         Py_RETURN_NONE;
121 }
122
123 PyDoc_STRVAR(pygpu_matrix_push_projection_doc,
124 "push_projection()\n"
125 "\n"
126 "   Add to the projection matrix stack.\n"
127 );
128 static PyObject *pygpu_matrix_push_projection(PyObject *UNUSED(self))
129 {
130         if (!pygpu_stack_is_push_projection_ok_or_error()) {
131                 return NULL;
132         }
133         GPU_matrix_push_projection();
134         Py_RETURN_NONE;
135 }
136
137 PyDoc_STRVAR(pygpu_matrix_pop_projection_doc,
138 "pop_projection()\n"
139 "\n"
140 "   Remove the last projection matrix from the stack.\n"
141 );
142 static PyObject *pygpu_matrix_pop_projection(PyObject *UNUSED(self))
143 {
144         if (!pygpu_stack_is_pop_projection_ok_or_error()) {
145                 return NULL;
146         }
147         GPU_matrix_pop_projection();
148         Py_RETURN_NONE;
149 }
150
151 /** \} */
152
153 /* -------------------------------------------------------------------- */
154 /** \name Stack (Context Manager)
155  *
156  * Safer alternative to ensure balanced push/pop calls.
157  *
158  * \{ */
159
160 typedef struct {
161         PyObject_HEAD /* required python macro */
162         int type;
163         int level;
164 } BPy_GPU_MatrixStackContext;
165
166 enum {
167         PYGPU_MATRIX_TYPE_MODEL_VIEW = 1,
168         PYGPU_MATRIX_TYPE_PROJECTION = 2,
169 };
170
171 static PyObject *pygpu_matrix_stack_context_enter(BPy_GPU_MatrixStackContext *self);
172 static PyObject *pygpu_matrix_stack_context_exit(BPy_GPU_MatrixStackContext *self, PyObject *args);
173
174 static PyMethodDef pygpu_matrix_stack_context_methods[] = {
175         {"__enter__", (PyCFunction)pygpu_matrix_stack_context_enter, METH_NOARGS},
176         {"__exit__",  (PyCFunction)pygpu_matrix_stack_context_exit,  METH_VARARGS},
177         {NULL}
178 };
179
180 static PyTypeObject pygpu_matrix_stack_context_Type = {
181         PyVarObject_HEAD_INIT(NULL, 0)
182         .tp_name = "GPUMatrixStackContext",
183         .tp_basicsize = sizeof(BPy_GPU_MatrixStackContext),
184         .tp_flags = Py_TPFLAGS_DEFAULT,
185         .tp_methods = pygpu_matrix_stack_context_methods,
186 };
187
188 static PyObject *pygpu_matrix_stack_context_enter(BPy_GPU_MatrixStackContext *self)
189 {
190         /* sanity - should never happen */
191         if (self->level != -1) {
192                 PyErr_SetString(PyExc_RuntimeError, "Already in use");
193                 return NULL;
194         }
195
196         if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
197                 if (!pygpu_stack_is_push_model_view_ok_or_error()) {
198                         return NULL;
199                 }
200                 GPU_matrix_push();
201                 self->level = GPU_matrix_stack_level_get_model_view();
202         }
203         else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
204                 if (!pygpu_stack_is_push_projection_ok_or_error()) {
205                         return NULL;
206                 }
207                 GPU_matrix_push_projection();
208                 self->level = GPU_matrix_stack_level_get_projection();
209         }
210         else {
211                 BLI_assert(0);
212         }
213         Py_RETURN_NONE;
214 }
215
216 static PyObject *pygpu_matrix_stack_context_exit(BPy_GPU_MatrixStackContext *self, PyObject *UNUSED(args))
217 {
218         /* sanity - should never happen */
219         if (self->level == -1) {
220                 fprintf(stderr, "Not yet in use\n");
221                 goto finally;
222         }
223
224         if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
225                 const int level = GPU_matrix_stack_level_get_model_view();
226                 if (level != self->level) {
227                         fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
228                 }
229                 if (level != 0) {
230                         GPU_matrix_pop();
231                 }
232         }
233         else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
234                 const int level = GPU_matrix_stack_level_get_projection();
235                 if (level != self->level) {
236                         fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
237                 }
238                 if (level != 0) {
239                         GPU_matrix_pop_projection();
240                 }
241         }
242         else {
243                 BLI_assert(0);
244         }
245 finally:
246         Py_RETURN_NONE;
247 }
248
249 static PyObject *pygpu_matrix_push_pop_impl(int type)
250 {
251         BPy_GPU_MatrixStackContext *ret = PyObject_New(BPy_GPU_MatrixStackContext, &pygpu_matrix_stack_context_Type);
252         ret->type = type;
253         ret->level = -1;
254         return (PyObject *)ret;
255 }
256
257 PyDoc_STRVAR(pygpu_matrix_push_pop_doc,
258 "push_pop()\n"
259 "\n"
260 "   Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
261 );
262 static PyObject *pygpu_matrix_push_pop(PyObject *UNUSED(self))
263 {
264         return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
265 }
266
267 PyDoc_STRVAR(pygpu_matrix_push_pop_projection_doc,
268 "push_pop_projection()\n"
269 "\n"
270 "   Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
271 );
272 static PyObject *pygpu_matrix_push_pop_projection(PyObject *UNUSED(self))
273 {
274         return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
275 }
276
277 /** \} */
278
279 /* -------------------------------------------------------------------- */
280 /** \name Manipulate State
281  * \{ */
282
283 PyDoc_STRVAR(pygpu_matrix_multiply_matrix_doc,
284 "multiply_matrix(matrix)\n"
285 "\n"
286 "   Multiply the current stack matrix.\n"
287 "\n"
288 "   :param matrix: A 4x4 matrix.\n"
289 "   :type matrix: :class:`mathutils.Matrix`\n"
290 );
291 static PyObject *pygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value)
292 {
293         MatrixObject *pymat;
294         if (!Matrix_Parse4x4(value, &pymat)) {
295                 return NULL;
296         }
297         GPU_matrix_mul(pymat->matrix);
298         Py_RETURN_NONE;
299 }
300
301 PyDoc_STRVAR(pygpu_matrix_scale_doc,
302 "scale(scale)\n"
303 "\n"
304 "   Scale the current stack matrix.\n"
305 "\n"
306 "   :param scale: Scale the current stack matrix.\n"
307 "   :type scale: sequence of 2 or 3 floats\n"
308 );
309 static PyObject *pygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value)
310 {
311         float scale[3];
312         int len;
313         if ((len = mathutils_array_parse(scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1) {
314                 return NULL;
315         }
316         if (len == 2) {
317                 GPU_matrix_scale_2fv(scale);
318         }
319         else {
320                 GPU_matrix_scale_3fv(scale);
321         }
322         Py_RETURN_NONE;
323 }
324
325 PyDoc_STRVAR(pygpu_matrix_scale_uniform_doc,
326 "scale_uniform(scale)\n"
327 "\n"
328 "   :param scale: Scale the current stack matrix.\n"
329 "   :type scale: sequence of 2 or 3 floats\n"
330 );
331 static PyObject *pygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value)
332 {
333         float scalar;
334         if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
335                 PyErr_Format(PyExc_TypeError,
336                              "expected a number, not %.200s",
337                              Py_TYPE(value)->tp_name);
338                 return NULL;
339         }
340         GPU_matrix_scale_1f(scalar);
341         Py_RETURN_NONE;
342 }
343
344 PyDoc_STRVAR(pygpu_matrix_translate_doc,
345 "translate(offset)\n"
346 "\n"
347 "   Scale the current stack matrix.\n"
348 "\n"
349 "   :param offset: Translate the current stack matrix.\n"
350 "   :type offset: sequence of 2 or 3 floats\n"
351 );
352 static PyObject *pygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value)
353 {
354         float offset[3];
355         int len;
356         if ((len = mathutils_array_parse(offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1) {
357                 return NULL;
358         }
359         if (len == 2) {
360                 GPU_matrix_translate_2fv(offset);
361         }
362         else {
363                 GPU_matrix_translate_3fv(offset);
364         }
365         Py_RETURN_NONE;
366 }
367
368 /** \} */
369
370 /* -------------------------------------------------------------------- */
371 /** \name Write State
372  * \{ */
373
374 PyDoc_STRVAR(pygpu_matrix_reset_doc,
375 "reset()\n"
376 "\n"
377 "   Empty stack and set to identity.\n"
378 );
379 static PyObject *pygpu_matrix_reset(PyObject *UNUSED(self))
380 {
381         GPU_matrix_reset();
382         Py_RETURN_NONE;
383 }
384
385 PyDoc_STRVAR(pygpu_matrix_load_identity_doc,
386 "load_identity()\n"
387 "\n"
388 "   Empty stack and set to identity.\n"
389 );
390 static PyObject *pygpu_matrix_load_identity(PyObject *UNUSED(self))
391 {
392         GPU_matrix_identity_set();
393         Py_RETURN_NONE;
394 }
395
396 PyDoc_STRVAR(pygpu_matrix_load_matrix_doc,
397 "load_matrix(matrix)\n"
398 "\n"
399 "   Load a matrix into the stack.\n"
400 "\n"
401 "   :param matrix: A 4x4 matrix.\n"
402 "   :type matrix: :class:`mathutils.Matrix`\n"
403 );
404 static PyObject *pygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value)
405 {
406         MatrixObject *pymat;
407         if (!Matrix_Parse4x4(value, &pymat)) {
408                 return NULL;
409         }
410         GPU_matrix_set(pymat->matrix);
411         Py_RETURN_NONE;
412 }
413
414 /** \} */
415
416 /* -------------------------------------------------------------------- */
417 /** \name Read State
418  * \{ */
419
420 PyDoc_STRVAR(pygpu_matrix_get_projection_matrix_doc,
421 "get_projection_matrix()\n"
422 "\n"
423 "   Return a copy of the projection matrix.\n"
424 "\n"
425 "   :return: A 4x4 projection matrix.\n"
426 "   :rtype: :class:`mathutils.Matrix`\n"
427 );
428 static PyObject *pygpu_matrix_get_projection_matrix(PyObject *UNUSED(self))
429 {
430         float matrix[4][4];
431         GPU_matrix_model_view_get(matrix);
432         return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
433 }
434
435
436 PyDoc_STRVAR(pygpu_matrix_get_modal_view_matrix_doc,
437 "get_view_matrix()\n"
438 "\n"
439 "   Return a copy of the view matrix.\n"
440 "\n"
441 "   :return: A 4x4 view matrix.\n"
442 "   :rtype: :class:`mathutils.Matrix`\n"
443 );
444 static PyObject *pygpu_matrix_get_modal_view_matrix(PyObject *UNUSED(self))
445 {
446         float matrix[4][4];
447         GPU_matrix_projection_get(matrix);
448         return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
449 }
450
451 PyDoc_STRVAR(pygpu_matrix_get_normal_matrix_doc,
452 "get_normal_matrix()\n"
453 "\n"
454 "   Return a copy of the normal matrix.\n"
455 "\n"
456 "   :return: A 3x3 normal matrix.\n"
457 "   :rtype: :class:`mathutils.Matrix`\n"
458 );
459 static PyObject *pygpu_matrix_get_normal_matrix(PyObject *UNUSED(self))
460 {
461         float matrix[3][3];
462         GPU_matrix_normal_get(matrix);
463         return Matrix_CreatePyObject(&matrix[0][0], 3, 3, NULL);
464 }
465
466 /** \} */
467
468 /* -------------------------------------------------------------------- */
469 /** \name Module
470  * \{ */
471
472 static struct PyMethodDef BPy_GPU_matrix_methods[] = {
473         /* Manage Stack */
474         {"push", (PyCFunction)pygpu_matrix_push,
475          METH_NOARGS, pygpu_matrix_push_doc},
476         {"pop", (PyCFunction)pygpu_matrix_pop,
477          METH_NOARGS, pygpu_matrix_pop_doc},
478
479         {"push_projection", (PyCFunction)pygpu_matrix_push_projection,
480          METH_NOARGS, pygpu_matrix_push_projection_doc},
481         {"pop_projection", (PyCFunction)pygpu_matrix_pop_projection,
482          METH_NOARGS, pygpu_matrix_pop_projection_doc},
483
484         /* Stack (Context Manager) */
485         {"push_pop", (PyCFunction)pygpu_matrix_push_pop,
486          METH_NOARGS, pygpu_matrix_push_pop_doc},
487         {"push_pop_projection", (PyCFunction)pygpu_matrix_push_pop_projection,
488          METH_NOARGS, pygpu_matrix_push_pop_projection_doc},
489
490         /* Manipulate State */
491         {"multiply_matrix", (PyCFunction)pygpu_matrix_multiply_matrix,
492          METH_O, pygpu_matrix_multiply_matrix_doc},
493         {"scale", (PyCFunction)pygpu_matrix_scale,
494          METH_O, pygpu_matrix_scale_doc},
495         {"scale_uniform", (PyCFunction)pygpu_matrix_scale_uniform,
496          METH_O, pygpu_matrix_scale_uniform_doc},
497         {"translate", (PyCFunction)pygpu_matrix_translate,
498          METH_O, pygpu_matrix_translate_doc},
499
500         /* TODO */
501 #if 0
502         {"rotate", (PyCFunction)pygpu_matrix_rotate,
503          METH_O, pygpu_matrix_rotate_doc},
504         {"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis,
505          METH_O, pygpu_matrix_rotate_axis_doc},
506         {"look_at", (PyCFunction)pygpu_matrix_look_at,
507          METH_O, pygpu_matrix_look_at_doc},
508 #endif
509
510         /* Write State */
511         {"reset", (PyCFunction)pygpu_matrix_reset,
512          METH_NOARGS, pygpu_matrix_reset_doc},
513         {"load_identity", (PyCFunction)pygpu_matrix_load_identity,
514          METH_NOARGS, pygpu_matrix_load_identity_doc},
515         {"load_matrix", (PyCFunction)pygpu_matrix_load_matrix,
516          METH_O, pygpu_matrix_load_matrix_doc},
517
518         /* Read State */
519         {"get_projection_matrix", (PyCFunction)pygpu_matrix_get_projection_matrix,
520          METH_NOARGS, pygpu_matrix_get_projection_matrix_doc},
521         {"get_model_view_matrix", (PyCFunction)pygpu_matrix_get_modal_view_matrix,
522          METH_NOARGS, pygpu_matrix_get_modal_view_matrix_doc},
523         {"get_normal_matrix", (PyCFunction)pygpu_matrix_get_normal_matrix,
524          METH_NOARGS, pygpu_matrix_get_normal_matrix_doc},
525
526         {NULL, NULL, 0, NULL}
527 };
528
529 PyDoc_STRVAR(BPy_GPU_matrix_doc,
530 "This module provides access to the matrix stack."
531 );
532 static PyModuleDef BPy_GPU_matrix_module_def = {
533         PyModuleDef_HEAD_INIT,
534         .m_name = "gpu.matrix",
535         .m_doc = BPy_GPU_matrix_doc,
536         .m_methods = BPy_GPU_matrix_methods,
537 };
538
539 PyObject *BPyInit_gpu_matrix(void)
540 {
541         PyObject *submodule;
542
543         submodule = PyModule_Create(&BPy_GPU_matrix_module_def);
544
545         if (PyType_Ready(&pygpu_matrix_stack_context_Type) < 0) {
546                 return NULL;
547         }
548
549         return submodule;
550 }
551
552 /** \} */