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