Blender V5.0
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
16
17#include <Python.h>
18
19#include "BLI_utildefines.h"
20
22
23#define USE_GPU_PY_MATRIX_API
24#include "GPU_matrix.hh"
25#undef USE_GPU_PY_MATRIX_API
26
27#include "gpu_py.hh"
28#include "gpu_py_matrix.hh" /* own include */
29
30/* -------------------------------------------------------------------- */
33
35{
36 if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
37 PyErr_SetString(
38 PyExc_RuntimeError,
39 "Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
40 return false;
41 }
42 return true;
43}
44
46{
47 if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
48 PyErr_SetString(
49 PyExc_RuntimeError,
50 "Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
51 return false;
52 }
53 return true;
54}
55
57{
59 PyErr_SetString(PyExc_RuntimeError, "Minimum model-view stack depth reached");
60 return false;
61 }
62 return true;
63}
64
66{
68 PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached");
69 return false;
70 }
71 return true;
72}
73
75
76/* -------------------------------------------------------------------- */
79
81 /* Wrap. */
82 pygpu_matrix_push_doc,
83 ".. function:: push()\n"
84 "\n"
85 " Add to the model-view matrix stack.\n");
86static PyObject *pygpu_matrix_push(PyObject * /*self*/)
87{
89
91 return nullptr;
92 }
94 Py_RETURN_NONE;
95}
96
98 /* Wrap. */
99 pygpu_matrix_pop_doc,
100 ".. function:: pop()\n"
101 "\n"
102 " Remove the last model-view matrix from the stack.\n");
103static PyObject *pygpu_matrix_pop(PyObject * /*self*/)
104{
106
108 return nullptr;
109 }
111 Py_RETURN_NONE;
112}
113
115 /* Wrap. */
116 pygpu_matrix_push_projection_doc,
117 ".. function:: push_projection()\n"
118 "\n"
119 " Add to the projection matrix stack.\n");
120static PyObject *pygpu_matrix_push_projection(PyObject * /*self*/)
121{
123
125 return nullptr;
126 }
128 Py_RETURN_NONE;
129}
130
132 /* Wrap. */
133 pygpu_matrix_pop_projection_doc,
134 ".. function:: pop_projection()\n"
135 "\n"
136 " Remove the last projection matrix from the stack.\n");
137static PyObject *pygpu_matrix_pop_projection(PyObject * /*self*/)
138{
140
142 return nullptr;
143 }
145 Py_RETURN_NONE;
146}
147
149
150/* -------------------------------------------------------------------- */
156
158 PyObject_HEAD /* Required Python macro. */
159 int type;
160 int level;
161};
162
163enum {
166};
167
169static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
170
171#ifdef __GNUC__
172# ifdef __clang__
173# pragma clang diagnostic push
174# pragma clang diagnostic ignored "-Wcast-function-type"
175# else
176# pragma GCC diagnostic push
177# pragma GCC diagnostic ignored "-Wcast-function-type"
178# endif
179#endif
180
182 {"__enter__", (PyCFunction)pygpu_matrix_stack_context_enter, METH_NOARGS},
183 {"__exit__", (PyCFunction)pygpu_matrix_stack_context_exit, METH_VARARGS},
184 {nullptr},
185};
186
187#ifdef __GNUC__
188# ifdef __clang__
189# pragma clang diagnostic pop
190# else
191# pragma GCC diagnostic pop
192# endif
193#endif
194
195static PyTypeObject PyGPUMatrixStackContext_Type = {
196 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
197 /*tp_name*/ "GPUMatrixStackContext",
198 /*tp_basicsize*/ sizeof(BPyGPU_MatrixStackContext),
199 /*tp_itemsize*/ 0,
200 /*tp_dealloc*/ nullptr,
201 /*tp_vectorcall_offset*/ 0,
202 /*tp_getattr*/ nullptr,
203 /*tp_setattr*/ nullptr,
204 /*tp_as_async*/ nullptr,
205 /*tp_repr*/ nullptr,
206 /*tp_as_number*/ nullptr,
207 /*tp_as_sequence*/ nullptr,
208 /*tp_as_mapping*/ nullptr,
209 /*tp_hash*/ nullptr,
210 /*tp_call*/ nullptr,
211 /*tp_str*/ nullptr,
212 /*tp_getattro*/ nullptr,
213 /*tp_setattro*/ nullptr,
214 /*tp_as_buffer*/ nullptr,
215 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
216 /*tp_doc*/ nullptr,
217 /*tp_traverse*/ nullptr,
218 /*tp_clear*/ nullptr,
219 /*tp_richcompare*/ nullptr,
220 /*tp_weaklistoffset*/ 0,
221 /*tp_iter*/ nullptr,
222 /*tp_iternext*/ nullptr,
224 /*tp_members*/ nullptr,
225 /*tp_getset*/ nullptr,
226 /*tp_base*/ nullptr,
227 /*tp_dict*/ nullptr,
228 /*tp_descr_get*/ nullptr,
229 /*tp_descr_set*/ nullptr,
230 /*tp_dictoffset*/ 0,
231 /*tp_init*/ nullptr,
232 /*tp_alloc*/ nullptr,
233 /*tp_new*/ nullptr,
234 /*tp_free*/ nullptr,
235 /*tp_is_gc*/ nullptr,
236 /*tp_bases*/ nullptr,
237 /*tp_mro*/ nullptr,
238 /*tp_cache*/ nullptr,
239 /*tp_subclasses*/ nullptr,
240 /*tp_weaklist*/ nullptr,
241 /*tp_del*/ nullptr,
242 /*tp_version_tag*/ 0,
243 /*tp_finalize*/ nullptr,
244 /*tp_vectorcall*/ nullptr,
245};
246
248{
250
251 /* sanity - should never happen */
252 if (self->level != -1) {
253 PyErr_SetString(PyExc_RuntimeError, "Already in use");
254 return nullptr;
255 }
256
257 if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
259 return nullptr;
260 }
263 }
264 else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
266 return nullptr;
267 }
270 }
271 else {
273 }
274 Py_RETURN_NONE;
275}
276
278 PyObject * /*args*/)
279{
281
282 /* sanity - should never happen */
283 if (self->level == -1) {
284 fprintf(stderr, "Not yet in use\n");
285 goto finally;
286 }
287
288 if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
289 const int level = GPU_matrix_stack_level_get_model_view();
290 if (level != self->level) {
291 fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
292 }
293 if (level != 0) {
295 }
296 }
297 else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
298 const int level = GPU_matrix_stack_level_get_projection();
299 if (level != self->level) {
300 fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
301 }
302 if (level != 0) {
304 }
305 }
306 else {
308 }
309finally:
310 Py_RETURN_NONE;
311}
312
313static PyObject *pygpu_matrix_push_pop_impl(int type)
314{
317 ret->type = type;
318 ret->level = -1;
319 return (PyObject *)ret;
320}
321
323 /* Wrap. */
324 pygpu_matrix_push_pop_doc,
325 ".. function:: push_pop()\n"
326 "\n"
327 " Context manager to ensure balanced push/pop calls, even in the case of an error.\n");
328static PyObject *pygpu_matrix_push_pop(PyObject * /*self*/)
329{
331
333}
334
336 /* Wrap. */
337 pygpu_matrix_push_pop_projection_doc,
338 ".. function:: push_pop_projection()\n"
339 "\n"
340 " Context manager to ensure balanced push/pop calls, even in the case of an error.\n");
341static PyObject *pygpu_matrix_push_pop_projection(PyObject * /*self*/)
342{
344}
345
347
348/* -------------------------------------------------------------------- */
351
353 /* Wrap. */
354 pygpu_matrix_multiply_matrix_doc,
355 ".. function:: multiply_matrix(matrix)\n"
356 "\n"
357 " Multiply the current stack matrix.\n"
358 "\n"
359 " :arg matrix: A 4x4 matrix.\n"
360 " :type matrix: :class:`mathutils.Matrix`\n");
361static PyObject *pygpu_matrix_multiply_matrix(PyObject * /*self*/, PyObject *value)
362{
364
365 MatrixObject *pymat;
366 if (!Matrix_Parse4x4(value, &pymat)) {
367 return nullptr;
368 }
369 GPU_matrix_mul(pymat->matrix);
370 Py_RETURN_NONE;
371}
372
374 /* Wrap. */
375 pygpu_matrix_scale_doc,
376 ".. function:: scale(scale)\n"
377 "\n"
378 " Scale the current stack matrix.\n"
379 "\n"
380 " :arg scale: Scale the current stack matrix with 2 or 3 floats.\n"
381 " :type scale: Sequence[float]\n");
382static PyObject *pygpu_matrix_scale(PyObject * /*self*/, PyObject *value)
383{
385
386 float scale[3];
387 int len;
389 scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1)
390 {
391 return nullptr;
392 }
393 if (len == 2) {
395 }
396 else {
398 }
399 Py_RETURN_NONE;
400}
401
403 /* Wrap. */
404 pygpu_matrix_scale_uniform_doc,
405 ".. function:: scale_uniform(scale)\n"
406 "\n"
407 " :arg scale: Scale the current stack matrix.\n"
408 " :type scale: float\n");
409static PyObject *pygpu_matrix_scale_uniform(PyObject * /*self*/, PyObject *value)
410{
412
413 float scalar;
414 if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
415 PyErr_Format(PyExc_TypeError, "expected a number, not %.200s", Py_TYPE(value)->tp_name);
416 return nullptr;
417 }
418 GPU_matrix_scale_1f(scalar);
419 Py_RETURN_NONE;
420}
421
423 /* Wrap. */
424 pygpu_matrix_translate_doc,
425 ".. function:: translate(offset)\n"
426 "\n"
427 " Scale the current stack matrix.\n"
428 "\n"
429 " :arg offset: Translate the current stack matrix with 2 or 3 floats.\n"
430 " :type offset: Sequence[float]\n");
431static PyObject *pygpu_matrix_translate(PyObject * /*self*/, PyObject *value)
432{
433 float offset[3];
434 int len;
436 offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1)
437 {
438 return nullptr;
439 }
440 if (len == 2) {
442 }
443 else {
445 }
446 Py_RETURN_NONE;
447}
448
450
451/* -------------------------------------------------------------------- */
454
456 /* Wrap. */
457 pygpu_matrix_reset_doc,
458 ".. function:: reset()\n"
459 "\n"
460 " Empty stack and set to identity.\n");
461static PyObject *pygpu_matrix_reset(PyObject * /*self*/)
462{
464
466 Py_RETURN_NONE;
467}
468
470 /* Wrap. */
471 pygpu_matrix_load_identity_doc,
472 ".. function:: load_identity()\n"
473 "\n"
474 " Load an identity matrix into the stack.\n");
475static PyObject *pygpu_matrix_load_identity(PyObject * /*self*/)
476{
478
480 Py_RETURN_NONE;
481}
482
484 /* Wrap. */
485 pygpu_matrix_load_matrix_doc,
486 ".. function:: load_matrix(matrix)\n"
487 "\n"
488 " Load a matrix into the stack.\n"
489 "\n"
490 " :arg matrix: A 4x4 matrix.\n"
491 " :type matrix: :class:`mathutils.Matrix`\n");
492static PyObject *pygpu_matrix_load_matrix(PyObject * /*self*/, PyObject *value)
493{
495
496 MatrixObject *pymat;
497 if (!Matrix_Parse4x4(value, &pymat)) {
498 return nullptr;
499 }
500 GPU_matrix_set(pymat->matrix);
501 Py_RETURN_NONE;
502}
503
505 /* Wrap. */
506 pygpu_matrix_load_projection_matrix_doc,
507 ".. function:: load_projection_matrix(matrix)\n"
508 "\n"
509 " Load a projection matrix into the stack.\n"
510 "\n"
511 " :arg matrix: A 4x4 matrix.\n"
512 " :type matrix: :class:`mathutils.Matrix`\n");
513static PyObject *pygpu_matrix_load_projection_matrix(PyObject * /*self*/, PyObject *value)
514{
516
517 MatrixObject *pymat;
518 if (!Matrix_Parse4x4(value, &pymat)) {
519 return nullptr;
520 }
521 GPU_matrix_projection_set(pymat->matrix);
522 Py_RETURN_NONE;
523}
524
526
527/* -------------------------------------------------------------------- */
530
532 /* Wrap. */
533 pygpu_matrix_get_projection_matrix_doc,
534 ".. function:: get_projection_matrix()\n"
535 "\n"
536 " Return a copy of the projection matrix.\n"
537 "\n"
538 " :return: A 4x4 projection matrix.\n"
539 " :rtype: :class:`mathutils.Matrix`\n");
540static PyObject *pygpu_matrix_get_projection_matrix(PyObject * /*self*/)
541{
543
544 float matrix[4][4];
546 return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
547}
548
550 /* Wrap. */
551 pygpu_matrix_get_model_view_matrix_doc,
552 ".. function:: get_model_view_matrix()\n"
553 "\n"
554 " Return a copy of the model-view matrix.\n"
555 "\n"
556 " :return: A 4x4 view matrix.\n"
557 " :rtype: :class:`mathutils.Matrix`\n");
558static PyObject *pygpu_matrix_get_model_view_matrix(PyObject * /*self*/)
559{
561
562 float matrix[4][4];
564 return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
565}
566
568 /* Wrap. */
569 pygpu_matrix_get_normal_matrix_doc,
570 ".. function:: get_normal_matrix()\n"
571 "\n"
572 " Return a copy of the normal matrix.\n"
573 "\n"
574 " :return: A 3x3 normal matrix.\n"
575 " :rtype: :class:`mathutils.Matrix`\n");
576static PyObject *pygpu_matrix_get_normal_matrix(PyObject * /*self*/)
577{
579
580 float matrix[3][3];
581 GPU_matrix_normal_get(matrix);
582 return Matrix_CreatePyObject(&matrix[0][0], 3, 3, nullptr);
583}
584
586
587/* -------------------------------------------------------------------- */
590
591#ifdef __GNUC__
592# ifdef __clang__
593# pragma clang diagnostic push
594# pragma clang diagnostic ignored "-Wcast-function-type"
595# else
596# pragma GCC diagnostic push
597# pragma GCC diagnostic ignored "-Wcast-function-type"
598# endif
599#endif
600
601static PyMethodDef pygpu_matrix__tp_methods[] = {
602 /* Manage Stack */
603 {"push", (PyCFunction)pygpu_matrix_push, METH_NOARGS, pygpu_matrix_push_doc},
604 {"pop", (PyCFunction)pygpu_matrix_pop, METH_NOARGS, pygpu_matrix_pop_doc},
605
606 {"push_projection",
607 (PyCFunction)pygpu_matrix_push_projection,
608 METH_NOARGS,
609 pygpu_matrix_push_projection_doc},
610 {"pop_projection",
611 (PyCFunction)pygpu_matrix_pop_projection,
612 METH_NOARGS,
613 pygpu_matrix_pop_projection_doc},
614
615 /* Stack (Context Manager) */
616 {"push_pop", (PyCFunction)pygpu_matrix_push_pop, METH_NOARGS, pygpu_matrix_push_pop_doc},
617 {"push_pop_projection",
619 METH_NOARGS,
620 pygpu_matrix_push_pop_projection_doc},
621
622 /* Manipulate State */
623 {"multiply_matrix",
624 (PyCFunction)pygpu_matrix_multiply_matrix,
625 METH_O,
626 pygpu_matrix_multiply_matrix_doc},
627 {"scale", (PyCFunction)pygpu_matrix_scale, METH_O, pygpu_matrix_scale_doc},
628 {"scale_uniform",
629 (PyCFunction)pygpu_matrix_scale_uniform,
630 METH_O,
631 pygpu_matrix_scale_uniform_doc},
632 {"translate", (PyCFunction)pygpu_matrix_translate, METH_O, pygpu_matrix_translate_doc},
633
634/* TODO */
635#if 0
636 {"rotate", (PyCFunction)pygpu_matrix_rotate, METH_O, pygpu_matrix_rotate_doc},
637 {"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis, METH_O, pygpu_matrix_rotate_axis_doc},
638 {"look_at", (PyCFunction)pygpu_matrix_look_at, METH_O, pygpu_matrix_look_at_doc},
639#endif
640
641 /* Write State */
642 {"reset", (PyCFunction)pygpu_matrix_reset, METH_NOARGS, pygpu_matrix_reset_doc},
643 {"load_identity",
644 (PyCFunction)pygpu_matrix_load_identity,
645 METH_NOARGS,
646 pygpu_matrix_load_identity_doc},
647 {"load_matrix", (PyCFunction)pygpu_matrix_load_matrix, METH_O, pygpu_matrix_load_matrix_doc},
648 {"load_projection_matrix",
650 METH_O,
651 pygpu_matrix_load_projection_matrix_doc},
652
653 /* Read State */
654 {"get_projection_matrix",
656 METH_NOARGS,
657 pygpu_matrix_get_projection_matrix_doc},
658 {"get_model_view_matrix",
660 METH_NOARGS,
661 pygpu_matrix_get_model_view_matrix_doc},
662 {"get_normal_matrix",
664 METH_NOARGS,
665 pygpu_matrix_get_normal_matrix_doc},
666
667 {nullptr, nullptr, 0, nullptr},
668};
669
670#ifdef __GNUC__
671# ifdef __clang__
672# pragma clang diagnostic pop
673# else
674# pragma GCC diagnostic pop
675# endif
676#endif
677
679 /* Wrap. */
680 pygpu_matrix__tp_doc,
681 "This module provides access to the matrix stack.");
682static PyModuleDef pygpu_matrix_module_def = {
683 /*m_base*/ PyModuleDef_HEAD_INIT,
684 /*m_name*/ "gpu.matrix",
685 /*m_doc*/ pygpu_matrix__tp_doc,
686 /*m_size*/ 0,
687 /*m_methods*/ pygpu_matrix__tp_methods,
688 /*m_slots*/ nullptr,
689 /*m_traverse*/ nullptr,
690 /*m_clear*/ nullptr,
691 /*m_free*/ nullptr,
692};
693
695{
696 PyObject *submodule;
697
698 submodule = PyModule_Create(&pygpu_matrix_module_def);
699
700 if (PyType_Ready(&PyGPUMatrixStackContext_Type) < 0) {
701 return nullptr;
702 }
703
704 return submodule;
705}
706
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define STRINGIFY(x)
#define GPU_matrix_normal_get(x)
void GPU_matrix_reset()
Definition gpu_matrix.cc:85
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 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:20
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 *)
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[]
@ PYGPU_MATRIX_TYPE_MODEL_VIEW
@ PYGPU_MATRIX_TYPE_PROJECTION
int mathutils_array_parse(float *array, int array_num_min, int array_num_max, PyObject *value, const char *error_prefix)
Definition mathutils.cc:96
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
uint len