Fix T67115: Changing the Shape of a Force Field object does not refresh the animation
[blender.git] / source / blender / python / gpu / gpu_py_element.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  * - Use ``bpygpu_`` for local API.
21  * - Use ``BPyGPU`` for public API.
22  */
23
24 #include <Python.h>
25
26 #include "GPU_element.h"
27
28 #include "BLI_math.h"
29
30 #include "MEM_guardedalloc.h"
31
32 #include "../generic/py_capi_utils.h"
33 #include "../generic/python_utildefines.h"
34
35 #include "gpu_py_api.h"
36 #include "gpu_py_element.h" /* own include */
37
38 /* -------------------------------------------------------------------- */
39 /** \name IndexBuf Type
40  * \{ */
41
42 static PyObject *bpygpu_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
43 {
44   BPYGPU_IS_INIT_OR_ERROR_OBJ;
45
46   const char *error_prefix = "IndexBuf.__new__";
47   bool ok = true;
48
49   struct {
50     GPUPrimType type_id;
51     PyObject *seq;
52   } params;
53
54   uint verts_per_prim;
55   uint index_len;
56   GPUIndexBufBuilder builder;
57
58   static const char *_keywords[] = {"type", "seq", NULL};
59   static _PyArg_Parser _parser = {"$O&O:IndexBuf.__new__", _keywords, 0};
60   if (!_PyArg_ParseTupleAndKeywordsFast(
61           args, kwds, &_parser, bpygpu_ParsePrimType, &params.type_id, &params.seq)) {
62     return NULL;
63   }
64
65   verts_per_prim = GPU_indexbuf_primitive_len(params.type_id);
66   if (verts_per_prim == -1) {
67     PyErr_Format(PyExc_ValueError,
68                  "The argument 'type' must be "
69                  "'POINTS', 'LINES', 'TRIS' or 'LINES_ADJ'");
70     return NULL;
71   }
72
73   if (PyObject_CheckBuffer(params.seq)) {
74     Py_buffer pybuffer;
75
76     if (PyObject_GetBuffer(params.seq, &pybuffer, PyBUF_FORMAT | PyBUF_ND) == -1) {
77       /* PyObject_GetBuffer already handles error messages. */
78       return NULL;
79     }
80
81     if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) {
82       PyErr_Format(PyExc_ValueError, "Each primitive must exactly %d indices", verts_per_prim);
83       return NULL;
84     }
85
86     if (pybuffer.itemsize != 4 ||
87         PyC_StructFmt_type_is_float_any(PyC_StructFmt_type_from_str(pybuffer.format))) {
88       PyErr_Format(PyExc_ValueError, "Each index must be an 4-bytes integer value");
89       return NULL;
90     }
91
92     index_len = pybuffer.shape[0];
93     if (pybuffer.ndim != 1) {
94       index_len *= pybuffer.shape[1];
95     }
96
97     /* The `vertex_len` parameter is only used for asserts in the Debug build. */
98     /* Not very useful in python since scripts are often tested in Release build. */
99     /* Use `INT_MAX` instead of the actual number of vertices. */
100     GPU_indexbuf_init(&builder, params.type_id, index_len, INT_MAX);
101
102 #if 0
103     uint *buf = pybuffer.buf;
104     for (uint i = index_len; i--; buf++) {
105       GPU_indexbuf_add_generic_vert(&builder, *buf);
106     }
107 #else
108     memcpy(builder.data, pybuffer.buf, index_len * sizeof(*builder.data));
109     builder.index_len = index_len;
110 #endif
111     PyBuffer_Release(&pybuffer);
112   }
113   else {
114     PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix);
115
116     if (seq_fast == NULL) {
117       return false;
118     }
119
120     const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
121
122     PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
123
124     index_len = seq_len * verts_per_prim;
125
126     /* The `vertex_len` parameter is only used for asserts in the Debug build. */
127     /* Not very useful in python since scripts are often tested in Release build. */
128     /* Use `INT_MAX` instead of the actual number of vertices. */
129     GPU_indexbuf_init(&builder, params.type_id, index_len, INT_MAX);
130
131     if (verts_per_prim == 1) {
132       for (uint i = 0; i < seq_len; i++) {
133         GPU_indexbuf_add_generic_vert(&builder, PyC_Long_AsU32(seq_items[i]));
134       }
135     }
136     else {
137       int values[4];
138       for (uint i = 0; i < seq_len; i++) {
139         PyObject *seq_fast_item = PySequence_Fast(seq_items[i], error_prefix);
140         if (seq_fast_item == NULL) {
141           PyErr_Format(PyExc_TypeError,
142                        "%s: expected a sequence, got %s",
143                        error_prefix,
144                        Py_TYPE(seq_items[i])->tp_name);
145           ok = false;
146           goto finally;
147         }
148
149         ok = PyC_AsArray_FAST(
150                  values, seq_fast_item, verts_per_prim, &PyLong_Type, false, error_prefix) == 0;
151
152         if (ok) {
153           for (uint j = 0; j < verts_per_prim; j++) {
154             GPU_indexbuf_add_generic_vert(&builder, values[j]);
155           }
156         }
157         Py_DECREF(seq_fast_item);
158       }
159     }
160
161     if (PyErr_Occurred()) {
162       ok = false;
163     }
164
165   finally:
166
167     Py_DECREF(seq_fast);
168   }
169
170   if (ok == false) {
171     MEM_freeN(builder.data);
172     return NULL;
173   }
174
175   return BPyGPUIndexBuf_CreatePyObject(GPU_indexbuf_build(&builder));
176 }
177
178 static void bpygpu_IndexBuf_dealloc(BPyGPUIndexBuf *self)
179 {
180   GPU_indexbuf_discard(self->elem);
181   Py_TYPE(self)->tp_free(self);
182 }
183
184 PyDoc_STRVAR(py_gpu_element_doc,
185              ".. class:: GPUIndexBuf(type, seq)\n"
186              "\n"
187              "   Contains an index buffer.\n"
188              "\n"
189              "   :param type: One of these primitive types: {\n"
190              "      `POINTS`,\n"
191              "      `LINES`,\n"
192              "      `TRIS`,\n"
193              "      `LINE_STRIP_ADJ` }\n"
194              "   :type type: `str`\n"
195              "   :param seq: Indices this index buffer will contain.\n"
196              "      Whether a 1D or 2D sequence is required depends on the type.\n"
197              "      Optionally the sequence can support the buffer protocol.\n"
198              "   :type seq: 1D or 2D sequence\n");
199 PyTypeObject BPyGPUIndexBuf_Type = {
200     PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUIndexBuf",
201     .tp_basicsize = sizeof(BPyGPUIndexBuf),
202     .tp_dealloc = (destructor)bpygpu_IndexBuf_dealloc,
203     .tp_flags = Py_TPFLAGS_DEFAULT,
204     .tp_doc = py_gpu_element_doc,
205     .tp_new = bpygpu_IndexBuf_new,
206 };
207
208 /** \} */
209
210 /* -------------------------------------------------------------------- */
211 /** \name Public API
212  * \{ */
213
214 PyObject *BPyGPUIndexBuf_CreatePyObject(GPUIndexBuf *elem)
215 {
216   BPyGPUIndexBuf *self;
217
218   self = PyObject_New(BPyGPUIndexBuf, &BPyGPUIndexBuf_Type);
219   self->elem = elem;
220
221   return (PyObject *)self;
222 }
223
224 /** \} */