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