Py API Docs: Fix formatting
[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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 ".. function:: 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 PyDoc_STRVAR(bpygpu_matrix_load_projection_matrix_doc,
418 ".. function:: load_projection_matrix(matrix)\n"
419 "\n"
420 "   Load a projection matrix into the stack.\n"
421 "\n"
422 "   :param matrix: A 4x4 matrix.\n"
423 "   :type matrix: :class:`mathutils.Matrix`\n"
424 );
425 static PyObject *bpygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value)
426 {
427         MatrixObject *pymat;
428         if (!Matrix_Parse4x4(value, &pymat)) {
429                 return NULL;
430         }
431         GPU_matrix_projection_set(pymat->matrix);
432         Py_RETURN_NONE;
433 }
434
435 /** \} */
436
437 /* -------------------------------------------------------------------- */
438 /** \name Read State
439  * \{ */
440
441 PyDoc_STRVAR(bpygpu_matrix_get_projection_matrix_doc,
442 ".. function:: get_projection_matrix()\n"
443 "\n"
444 "   Return a copy of the projection matrix.\n"
445 "\n"
446 "   :return: A 4x4 projection matrix.\n"
447 "   :rtype: :class:`mathutils.Matrix`\n"
448 );
449 static PyObject *bpygpu_matrix_get_projection_matrix(PyObject *UNUSED(self))
450 {
451         float matrix[4][4];
452         GPU_matrix_model_view_get(matrix);
453         return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
454 }
455
456
457 PyDoc_STRVAR(bpygpu_matrix_get_modal_view_matrix_doc,
458 ".. function:: get_view_matrix()\n"
459 "\n"
460 "   Return a copy of the view matrix.\n"
461 "\n"
462 "   :return: A 4x4 view matrix.\n"
463 "   :rtype: :class:`mathutils.Matrix`\n"
464 );
465 static PyObject *bpygpu_matrix_get_modal_view_matrix(PyObject *UNUSED(self))
466 {
467         float matrix[4][4];
468         GPU_matrix_projection_get(matrix);
469         return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
470 }
471
472 PyDoc_STRVAR(bpygpu_matrix_get_normal_matrix_doc,
473 ".. function:: get_normal_matrix()\n"
474 "\n"
475 "   Return a copy of the normal matrix.\n"
476 "\n"
477 "   :return: A 3x3 normal matrix.\n"
478 "   :rtype: :class:`mathutils.Matrix`\n"
479 );
480 static PyObject *bpygpu_matrix_get_normal_matrix(PyObject *UNUSED(self))
481 {
482         float matrix[3][3];
483         GPU_matrix_normal_get(matrix);
484         return Matrix_CreatePyObject(&matrix[0][0], 3, 3, NULL);
485 }
486
487 /** \} */
488
489 /* -------------------------------------------------------------------- */
490 /** \name Module
491  * \{ */
492
493 static struct PyMethodDef bpygpu_matrix_methods[] = {
494         /* Manage Stack */
495         {"push", (PyCFunction)bpygpu_matrix_push,
496          METH_NOARGS, bpygpu_matrix_push_doc},
497         {"pop", (PyCFunction)bpygpu_matrix_pop,
498          METH_NOARGS, bpygpu_matrix_pop_doc},
499
500         {"push_projection", (PyCFunction)bpygpu_matrix_push_projection,
501          METH_NOARGS, bpygpu_matrix_push_projection_doc},
502         {"pop_projection", (PyCFunction)bpygpu_matrix_pop_projection,
503          METH_NOARGS, bpygpu_matrix_pop_projection_doc},
504
505         /* Stack (Context Manager) */
506         {"push_pop", (PyCFunction)bpygpu_matrix_push_pop,
507          METH_NOARGS, bpygpu_matrix_push_pop_doc},
508         {"push_pop_projection", (PyCFunction)bpygpu_matrix_push_pop_projection,
509          METH_NOARGS, bpygpu_matrix_push_pop_projection_doc},
510
511         /* Manipulate State */
512         {"multiply_matrix", (PyCFunction)bpygpu_matrix_multiply_matrix,
513          METH_O, bpygpu_matrix_multiply_matrix_doc},
514         {"scale", (PyCFunction)bpygpu_matrix_scale,
515          METH_O, bpygpu_matrix_scale_doc},
516         {"scale_uniform", (PyCFunction)bpygpu_matrix_scale_uniform,
517          METH_O, bpygpu_matrix_scale_uniform_doc},
518         {"translate", (PyCFunction)bpygpu_matrix_translate,
519          METH_O, bpygpu_matrix_translate_doc},
520
521         /* TODO */
522 #if 0
523         {"rotate", (PyCFunction)bpygpu_matrix_rotate,
524          METH_O, bpygpu_matrix_rotate_doc},
525         {"rotate_axis", (PyCFunction)bpygpu_matrix_rotate_axis,
526          METH_O, bpygpu_matrix_rotate_axis_doc},
527         {"look_at", (PyCFunction)bpygpu_matrix_look_at,
528          METH_O, bpygpu_matrix_look_at_doc},
529 #endif
530
531         /* Write State */
532         {"reset", (PyCFunction)bpygpu_matrix_reset,
533          METH_NOARGS, bpygpu_matrix_reset_doc},
534         {"load_identity", (PyCFunction)bpygpu_matrix_load_identity,
535          METH_NOARGS, bpygpu_matrix_load_identity_doc},
536         {"load_matrix", (PyCFunction)bpygpu_matrix_load_matrix,
537          METH_O, bpygpu_matrix_load_matrix_doc},
538         {"load_projection_matrix", (PyCFunction)bpygpu_matrix_load_projection_matrix,
539          METH_O, bpygpu_matrix_load_projection_matrix_doc},
540
541         /* Read State */
542         {"get_projection_matrix", (PyCFunction)bpygpu_matrix_get_projection_matrix,
543          METH_NOARGS, bpygpu_matrix_get_projection_matrix_doc},
544         {"get_model_view_matrix", (PyCFunction)bpygpu_matrix_get_modal_view_matrix,
545          METH_NOARGS, bpygpu_matrix_get_modal_view_matrix_doc},
546         {"get_normal_matrix", (PyCFunction)bpygpu_matrix_get_normal_matrix,
547          METH_NOARGS, bpygpu_matrix_get_normal_matrix_doc},
548
549         {NULL, NULL, 0, NULL}
550 };
551
552 PyDoc_STRVAR(bpygpu_matrix_doc,
553 "This module provides access to the matrix stack."
554 );
555 static PyModuleDef BPyGPU_matrix_module_def = {
556         PyModuleDef_HEAD_INIT,
557         .m_name = "gpu.matrix",
558         .m_doc = bpygpu_matrix_doc,
559         .m_methods = bpygpu_matrix_methods,
560 };
561
562 PyObject *BPyInit_gpu_matrix(void)
563 {
564         PyObject *submodule;
565
566         submodule = PyModule_Create(&BPyGPU_matrix_module_def);
567
568         if (PyType_Ready(&BPyGPU_matrix_stack_context_Type) < 0) {
569                 return NULL;
570         }
571
572         return submodule;
573 }
574
575 /** \} */