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