Blender V4.3
gpu_py_matrix.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
17#include <Python.h>
18
19#include "BLI_utildefines.h"
20
22
24
25#define USE_GPU_PY_MATRIX_API
26#include "GPU_matrix.hh"
27#undef USE_GPU_PY_MATRIX_API
28
29#include "gpu_py.hh"
30#include "gpu_py_matrix.hh" /* own include */
31
32/* -------------------------------------------------------------------- */
37{
38 if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
39 PyErr_SetString(
40 PyExc_RuntimeError,
41 "Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
42 return false;
43 }
44 return true;
45}
46
48{
49 if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
50 PyErr_SetString(
51 PyExc_RuntimeError,
52 "Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
53 return false;
54 }
55 return true;
56}
57
59{
61 PyErr_SetString(PyExc_RuntimeError, "Minimum model-view stack depth reached");
62 return false;
63 }
64 return true;
65}
66
68{
70 PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached");
71 return false;
72 }
73 return true;
74}
75
78/* -------------------------------------------------------------------- */
83 /* Wrap. */
84 pygpu_matrix_push_doc,
85 ".. function:: push()\n"
86 "\n"
87 " Add to the model-view matrix stack.\n");
88static PyObject *pygpu_matrix_push(PyObject * /*self*/)
89{
91
93 return nullptr;
94 }
96 Py_RETURN_NONE;
97}
98
100 /* Wrap. */
101 pygpu_matrix_pop_doc,
102 ".. function:: pop()\n"
103 "\n"
104 " Remove the last model-view matrix from the stack.\n");
105static PyObject *pygpu_matrix_pop(PyObject * /*self*/)
106{
108
110 return nullptr;
111 }
113 Py_RETURN_NONE;
114}
115
117 /* Wrap. */
118 pygpu_matrix_push_projection_doc,
119 ".. function:: push_projection()\n"
120 "\n"
121 " Add to the projection matrix stack.\n");
122static PyObject *pygpu_matrix_push_projection(PyObject * /*self*/)
123{
125
127 return nullptr;
128 }
130 Py_RETURN_NONE;
131}
132
134 /* Wrap. */
135 pygpu_matrix_pop_projection_doc,
136 ".. function:: pop_projection()\n"
137 "\n"
138 " Remove the last projection matrix from the stack.\n");
139static PyObject *pygpu_matrix_pop_projection(PyObject * /*self*/)
140{
142
144 return nullptr;
145 }
147 Py_RETURN_NONE;
148}
149
152/* -------------------------------------------------------------------- */
160 PyObject_HEAD /* Required Python macro. */
161 int type;
162 int level;
163};
164
165enum {
168};
169
171static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
172
173#if (defined(__GNUC__) && !defined(__clang__))
174# pragma GCC diagnostic push
175# pragma GCC diagnostic ignored "-Wcast-function-type"
176#endif
177
179 {"__enter__", (PyCFunction)pygpu_matrix_stack_context_enter, METH_NOARGS},
180 {"__exit__", (PyCFunction)pygpu_matrix_stack_context_exit, METH_VARARGS},
181 {nullptr},
182};
183
184#if (defined(__GNUC__) && !defined(__clang__))
185# pragma GCC diagnostic pop
186#endif
187
188static PyTypeObject PyGPUMatrixStackContext_Type = {
189 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
190 /*tp_name*/ "GPUMatrixStackContext",
191 /*tp_basicsize*/ sizeof(BPyGPU_MatrixStackContext),
192 /*tp_itemsize*/ 0,
193 /*tp_dealloc*/ nullptr,
194 /*tp_vectorcall_offset*/ 0,
195 /*tp_getattr*/ nullptr,
196 /*tp_setattr*/ nullptr,
197 /*tp_as_async*/ nullptr,
198 /*tp_repr*/ nullptr,
199 /*tp_as_number*/ nullptr,
200 /*tp_as_sequence*/ nullptr,
201 /*tp_as_mapping*/ nullptr,
202 /*tp_hash*/ nullptr,
203 /*tp_call*/ nullptr,
204 /*tp_str*/ nullptr,
205 /*tp_getattro*/ nullptr,
206 /*tp_setattro*/ nullptr,
207 /*tp_as_buffer*/ nullptr,
208 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
209 /*tp_doc*/ nullptr,
210 /*tp_traverse*/ nullptr,
211 /*tp_clear*/ nullptr,
212 /*tp_richcompare*/ nullptr,
213 /*tp_weaklistoffset*/ 0,
214 /*tp_iter*/ nullptr,
215 /*tp_iternext*/ nullptr,
217 /*tp_members*/ nullptr,
218 /*tp_getset*/ nullptr,
219 /*tp_base*/ nullptr,
220 /*tp_dict*/ nullptr,
221 /*tp_descr_get*/ nullptr,
222 /*tp_descr_set*/ nullptr,
223 /*tp_dictoffset*/ 0,
224 /*tp_init*/ nullptr,
225 /*tp_alloc*/ nullptr,
226 /*tp_new*/ nullptr,
227 /*tp_free*/ nullptr,
228 /*tp_is_gc*/ nullptr,
229 /*tp_bases*/ nullptr,
230 /*tp_mro*/ nullptr,
231 /*tp_cache*/ nullptr,
232 /*tp_subclasses*/ nullptr,
233 /*tp_weaklist*/ nullptr,
234 /*tp_del*/ nullptr,
235 /*tp_version_tag*/ 0,
236 /*tp_finalize*/ nullptr,
237 /*tp_vectorcall*/ nullptr,
238};
239
241{
243
244 /* sanity - should never happen */
245 if (self->level != -1) {
246 PyErr_SetString(PyExc_RuntimeError, "Already in use");
247 return nullptr;
248 }
249
250 if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
252 return nullptr;
253 }
256 }
257 else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
259 return nullptr;
260 }
263 }
264 else {
266 }
267 Py_RETURN_NONE;
268}
269
271 PyObject * /*args*/)
272{
274
275 /* sanity - should never happen */
276 if (self->level == -1) {
277 fprintf(stderr, "Not yet in use\n");
278 goto finally;
279 }
280
281 if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
282 const int level = GPU_matrix_stack_level_get_model_view();
283 if (level != self->level) {
284 fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
285 }
286 if (level != 0) {
288 }
289 }
290 else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
291 const int level = GPU_matrix_stack_level_get_projection();
292 if (level != self->level) {
293 fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
294 }
295 if (level != 0) {
297 }
298 }
299 else {
301 }
302finally:
303 Py_RETURN_NONE;
304}
305
306static PyObject *pygpu_matrix_push_pop_impl(int type)
307{
310 ret->type = type;
311 ret->level = -1;
312 return (PyObject *)ret;
313}
314
316 /* Wrap. */
317 pygpu_matrix_push_pop_doc,
318 ".. function:: push_pop()\n"
319 "\n"
320 " Context manager to ensure balanced push/pop calls, even in the case of an error.\n");
321static PyObject *pygpu_matrix_push_pop(PyObject * /*self*/)
322{
324
326}
327
329 /* Wrap. */
330 pygpu_matrix_push_pop_projection_doc,
331 ".. function:: push_pop_projection()\n"
332 "\n"
333 " Context manager to ensure balanced push/pop calls, even in the case of an error.\n");
334static PyObject *pygpu_matrix_push_pop_projection(PyObject * /*self*/)
335{
337}
338
341/* -------------------------------------------------------------------- */
346 /* Wrap. */
347 pygpu_matrix_multiply_matrix_doc,
348 ".. function:: multiply_matrix(matrix)\n"
349 "\n"
350 " Multiply the current stack matrix.\n"
351 "\n"
352 " :arg matrix: A 4x4 matrix.\n"
353 " :type matrix: :class:`mathutils.Matrix`\n");
354static PyObject *pygpu_matrix_multiply_matrix(PyObject * /*self*/, PyObject *value)
355{
357
358 MatrixObject *pymat;
359 if (!Matrix_Parse4x4(value, &pymat)) {
360 return nullptr;
361 }
362 GPU_matrix_mul(pymat->matrix);
363 Py_RETURN_NONE;
364}
365
367 /* Wrap. */
368 pygpu_matrix_scale_doc,
369 ".. function:: scale(scale)\n"
370 "\n"
371 " Scale the current stack matrix.\n"
372 "\n"
373 " :arg scale: Scale the current stack matrix with 2 or 3 floats.\n"
374 " :type scale: Sequence[float]\n");
375static PyObject *pygpu_matrix_scale(PyObject * /*self*/, PyObject *value)
376{
378
379 float scale[3];
380 int len;
382 scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1)
383 {
384 return nullptr;
385 }
386 if (len == 2) {
388 }
389 else {
391 }
392 Py_RETURN_NONE;
393}
394
396 /* Wrap. */
397 pygpu_matrix_scale_uniform_doc,
398 ".. function:: scale_uniform(scale)\n"
399 "\n"
400 " :arg scale: Scale the current stack matrix.\n"
401 " :type scale: float\n");
402static PyObject *pygpu_matrix_scale_uniform(PyObject * /*self*/, PyObject *value)
403{
405
406 float scalar;
407 if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
408 PyErr_Format(PyExc_TypeError, "expected a number, not %.200s", Py_TYPE(value)->tp_name);
409 return nullptr;
410 }
411 GPU_matrix_scale_1f(scalar);
412 Py_RETURN_NONE;
413}
414
416 /* Wrap. */
417 pygpu_matrix_translate_doc,
418 ".. function:: translate(offset)\n"
419 "\n"
420 " Scale the current stack matrix.\n"
421 "\n"
422 " :arg offset: Translate the current stack matrix with 2 or 3 floats.\n"
423 " :type offset: Sequence[float]\n");
424static PyObject *pygpu_matrix_translate(PyObject * /*self*/, PyObject *value)
425{
426 float offset[3];
427 int len;
429 offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1)
430 {
431 return nullptr;
432 }
433 if (len == 2) {
435 }
436 else {
438 }
439 Py_RETURN_NONE;
440}
441
444/* -------------------------------------------------------------------- */
449 /* Wrap. */
450 pygpu_matrix_reset_doc,
451 ".. function:: reset()\n"
452 "\n"
453 " Empty stack and set to identity.\n");
454static PyObject *pygpu_matrix_reset(PyObject * /*self*/)
455{
457
459 Py_RETURN_NONE;
460}
461
463 /* Wrap. */
464 pygpu_matrix_load_identity_doc,
465 ".. function:: load_identity()\n"
466 "\n"
467 " Load an identity matrix into the stack.\n");
468static PyObject *pygpu_matrix_load_identity(PyObject * /*self*/)
469{
471
473 Py_RETURN_NONE;
474}
475
477 /* Wrap. */
478 pygpu_matrix_load_matrix_doc,
479 ".. function:: load_matrix(matrix)\n"
480 "\n"
481 " Load a matrix into the stack.\n"
482 "\n"
483 " :arg matrix: A 4x4 matrix.\n"
484 " :type matrix: :class:`mathutils.Matrix`\n");
485static PyObject *pygpu_matrix_load_matrix(PyObject * /*self*/, PyObject *value)
486{
488
489 MatrixObject *pymat;
490 if (!Matrix_Parse4x4(value, &pymat)) {
491 return nullptr;
492 }
493 GPU_matrix_set(pymat->matrix);
494 Py_RETURN_NONE;
495}
496
498 /* Wrap. */
499 pygpu_matrix_load_projection_matrix_doc,
500 ".. function:: load_projection_matrix(matrix)\n"
501 "\n"
502 " Load a projection matrix into the stack.\n"
503 "\n"
504 " :arg matrix: A 4x4 matrix.\n"
505 " :type matrix: :class:`mathutils.Matrix`\n");
506static PyObject *pygpu_matrix_load_projection_matrix(PyObject * /*self*/, PyObject *value)
507{
509
510 MatrixObject *pymat;
511 if (!Matrix_Parse4x4(value, &pymat)) {
512 return nullptr;
513 }
514 GPU_matrix_projection_set(pymat->matrix);
515 Py_RETURN_NONE;
516}
517
520/* -------------------------------------------------------------------- */
525 /* Wrap. */
526 pygpu_matrix_get_projection_matrix_doc,
527 ".. function:: get_projection_matrix()\n"
528 "\n"
529 " Return a copy of the projection matrix.\n"
530 "\n"
531 " :return: A 4x4 projection matrix.\n"
532 " :rtype: :class:`mathutils.Matrix`\n");
533static PyObject *pygpu_matrix_get_projection_matrix(PyObject * /*self*/)
534{
536
537 float matrix[4][4];
539 return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
540}
541
543 /* Wrap. */
544 pygpu_matrix_get_model_view_matrix_doc,
545 ".. function:: get_model_view_matrix()\n"
546 "\n"
547 " Return a copy of the model-view matrix.\n"
548 "\n"
549 " :return: A 4x4 view matrix.\n"
550 " :rtype: :class:`mathutils.Matrix`\n");
551static PyObject *pygpu_matrix_get_model_view_matrix(PyObject * /*self*/)
552{
554
555 float matrix[4][4];
557 return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
558}
559
561 /* Wrap. */
562 pygpu_matrix_get_normal_matrix_doc,
563 ".. function:: get_normal_matrix()\n"
564 "\n"
565 " Return a copy of the normal matrix.\n"
566 "\n"
567 " :return: A 3x3 normal matrix.\n"
568 " :rtype: :class:`mathutils.Matrix`\n");
569static PyObject *pygpu_matrix_get_normal_matrix(PyObject * /*self*/)
570{
572
573 float matrix[3][3];
574 GPU_matrix_normal_get(matrix);
575 return Matrix_CreatePyObject(&matrix[0][0], 3, 3, nullptr);
576}
577
580/* -------------------------------------------------------------------- */
584#if (defined(__GNUC__) && !defined(__clang__))
585# pragma GCC diagnostic push
586# pragma GCC diagnostic ignored "-Wcast-function-type"
587#endif
588
589static PyMethodDef pygpu_matrix__tp_methods[] = {
590 /* Manage Stack */
591 {"push", (PyCFunction)pygpu_matrix_push, METH_NOARGS, pygpu_matrix_push_doc},
592 {"pop", (PyCFunction)pygpu_matrix_pop, METH_NOARGS, pygpu_matrix_pop_doc},
593
594 {"push_projection",
595 (PyCFunction)pygpu_matrix_push_projection,
596 METH_NOARGS,
597 pygpu_matrix_push_projection_doc},
598 {"pop_projection",
599 (PyCFunction)pygpu_matrix_pop_projection,
600 METH_NOARGS,
601 pygpu_matrix_pop_projection_doc},
602
603 /* Stack (Context Manager) */
604 {"push_pop", (PyCFunction)pygpu_matrix_push_pop, METH_NOARGS, pygpu_matrix_push_pop_doc},
605 {"push_pop_projection",
607 METH_NOARGS,
608 pygpu_matrix_push_pop_projection_doc},
609
610 /* Manipulate State */
611 {"multiply_matrix",
612 (PyCFunction)pygpu_matrix_multiply_matrix,
613 METH_O,
614 pygpu_matrix_multiply_matrix_doc},
615 {"scale", (PyCFunction)pygpu_matrix_scale, METH_O, pygpu_matrix_scale_doc},
616 {"scale_uniform",
617 (PyCFunction)pygpu_matrix_scale_uniform,
618 METH_O,
619 pygpu_matrix_scale_uniform_doc},
620 {"translate", (PyCFunction)pygpu_matrix_translate, METH_O, pygpu_matrix_translate_doc},
621
622/* TODO */
623#if 0
624 {"rotate", (PyCFunction)pygpu_matrix_rotate, METH_O, pygpu_matrix_rotate_doc},
625 {"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis, METH_O, pygpu_matrix_rotate_axis_doc},
626 {"look_at", (PyCFunction)pygpu_matrix_look_at, METH_O, pygpu_matrix_look_at_doc},
627#endif
628
629 /* Write State */
630 {"reset", (PyCFunction)pygpu_matrix_reset, METH_NOARGS, pygpu_matrix_reset_doc},
631 {"load_identity",
632 (PyCFunction)pygpu_matrix_load_identity,
633 METH_NOARGS,
634 pygpu_matrix_load_identity_doc},
635 {"load_matrix", (PyCFunction)pygpu_matrix_load_matrix, METH_O, pygpu_matrix_load_matrix_doc},
636 {"load_projection_matrix",
638 METH_O,
639 pygpu_matrix_load_projection_matrix_doc},
640
641 /* Read State */
642 {"get_projection_matrix",
644 METH_NOARGS,
645 pygpu_matrix_get_projection_matrix_doc},
646 {"get_model_view_matrix",
648 METH_NOARGS,
649 pygpu_matrix_get_model_view_matrix_doc},
650 {"get_normal_matrix",
652 METH_NOARGS,
653 pygpu_matrix_get_normal_matrix_doc},
654
655 {nullptr, nullptr, 0, nullptr},
656};
657
658#if (defined(__GNUC__) && !defined(__clang__))
659# pragma GCC diagnostic pop
660#endif
661
663 /* Wrap. */
664 pygpu_matrix__tp_doc,
665 "This module provides access to the matrix stack.");
666static PyModuleDef pygpu_matrix_module_def = {
667 /*m_base*/ PyModuleDef_HEAD_INIT,
668 /*m_name*/ "gpu.matrix",
669 /*m_doc*/ pygpu_matrix__tp_doc,
670 /*m_size*/ 0,
671 /*m_methods*/ pygpu_matrix__tp_methods,
672 /*m_slots*/ nullptr,
673 /*m_traverse*/ nullptr,
674 /*m_clear*/ nullptr,
675 /*m_free*/ nullptr,
676};
677
679{
680 PyObject *submodule;
681
682 submodule = PyModule_Create(&pygpu_matrix_module_def);
683
684 if (PyType_Ready(&PyGPUMatrixStackContext_Type) < 0) {
685 return nullptr;
686 }
687
688 return submodule;
689}
690
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define STRINGIFY(x)
#define GPU_matrix_normal_get(x)
void GPU_matrix_reset()
Definition gpu_matrix.cc:87
void GPU_matrix_translate_2fv(const float vec[2])
#define GPU_matrix_model_view_get(x)
void GPU_matrix_identity_set()
void GPU_matrix_scale_2fv(const float vec[2])
#define GPU_matrix_set(x)
void GPU_matrix_push()
void GPU_matrix_push_projection()
#define GPU_matrix_mul(x)
void GPU_matrix_scale_3fv(const float vec[3])
void GPU_matrix_scale_1f(float factor)
void GPU_matrix_pop_projection()
#define GPU_matrix_projection_get(x)
#define GPU_matrix_projection_set(x)
void GPU_matrix_translate_3fv(const float vec[3])
void GPU_matrix_pop()
PyObject * self
int len
int GPU_matrix_stack_level_get_projection()
int GPU_matrix_stack_level_get_model_view()
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition gpu_py.hh:18
static PyObject * pygpu_matrix_load_projection_matrix(PyObject *, PyObject *value)
static bool pygpu_stack_is_push_model_view_ok_or_error()
static PyObject * pygpu_matrix_push_pop_impl(int type)
static PyObject * pygpu_matrix_pop(PyObject *)
static PyObject * pygpu_matrix_push_projection(PyObject *)
static bool pygpu_stack_is_pop_model_view_ok_or_error()
PyDoc_STRVAR(pygpu_matrix_push_doc, ".. function:: push()\n" "\n" " Add to the model-view matrix stack.\n")
static PyObject * pygpu_matrix_multiply_matrix(PyObject *, PyObject *value)
static PyObject * pygpu_matrix_load_identity(PyObject *)
static PyObject * pygpu_matrix_load_matrix(PyObject *, PyObject *value)
static PyObject * pygpu_matrix_scale(PyObject *, PyObject *value)
static PyMethodDef pygpu_matrix_stack_context__tp_methods[]
static PyObject * pygpu_matrix_push_pop_projection(PyObject *)
static bool pygpu_stack_is_pop_projection_ok_or_error()
static PyObject * pygpu_matrix_pop_projection(PyObject *)
static PyModuleDef pygpu_matrix_module_def
static PyObject * pygpu_matrix_get_normal_matrix(PyObject *)
static PyObject * pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args)
static PyTypeObject PyGPUMatrixStackContext_Type
static PyObject * pygpu_matrix_get_projection_matrix(PyObject *)
@ PYGPU_MATRIX_TYPE_MODEL_VIEW
@ PYGPU_MATRIX_TYPE_PROJECTION
static PyObject * pygpu_matrix_get_model_view_matrix(PyObject *)
static PyObject * pygpu_matrix_translate(PyObject *, PyObject *value)
PyObject * bpygpu_matrix_init()
static PyObject * pygpu_matrix_reset(PyObject *)
static PyObject * pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self)
static PyObject * pygpu_matrix_push_pop(PyObject *)
static bool pygpu_stack_is_push_projection_ok_or_error()
static PyObject * pygpu_matrix_push(PyObject *)
static PyObject * pygpu_matrix_scale_uniform(PyObject *, PyObject *value)
static PyMethodDef pygpu_matrix__tp_methods[]
int mathutils_array_parse(float *array, int array_num_min, int array_num_max, PyObject *value, const char *error_prefix)
Definition mathutils.cc:97
PyObject * Matrix_CreatePyObject(const float *mat, const ushort col_num, const ushort row_num, PyTypeObject *base_type)
int Matrix_Parse4x4(PyObject *o, void *p)
return ret