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