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