PyAPI: Iniital gawain API for Python
[blender.git] / source / blender / python / gawain / gwn_py_types.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/gawain/gwn_py_types.c
22  *  \ingroup pygawain
23  */
24
25 #include <Python.h>
26
27 #include "gawain/gwn_batch.h"
28 #include "gawain/gwn_vertex_format.h"
29
30 #include "BLI_math.h"
31
32 #include "GPU_batch.h"
33
34 #include "MEM_guardedalloc.h"
35
36 #include "../generic/py_capi_utils.h"
37 #include "../generic/python_utildefines.h"
38
39 #include "gwn_py_types.h" /* own include */
40
41 #ifdef __BIG_ENDIAN__
42    /* big endian */
43 #  define MAKE_ID2(c, d)  ((c) << 8 | (d))
44 #  define MAKE_ID3(a, b, c) ( (int)(a) << 24 | (int)(b) << 16 | (c) << 8 )
45 #  define MAKE_ID4(a, b, c, d) ( (int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d) )
46 #else
47    /* little endian  */
48 #  define MAKE_ID2(c, d)  ((d) << 8 | (c))
49 #  define MAKE_ID3(a, b, c) ( (int)(c) << 16 | (b) << 8 | (a) )
50 #  define MAKE_ID4(a, b, c, d) ( (int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a) )
51 #endif
52
53 /* -------------------------------------------------------------------- */
54
55 /** \name Enum Conversion
56  *
57  * Use with PyArg_ParseTuple's "O&" formatting.
58  * \{ */
59
60 static int BPy_Gwn_ParseVertCompType(PyObject *o, void *p)
61 {
62         Py_ssize_t comp_type_id_len;
63         const char *comp_type_id = _PyUnicode_AsStringAndSize(o, &comp_type_id_len);
64         if (comp_type_id == NULL) {
65                 PyErr_Format(PyExc_ValueError,
66                              "expected a string, got %s",
67                              Py_TYPE(o)->tp_name);
68                 return 0;
69         }
70
71         Gwn_VertCompType comp_type;
72         if (comp_type_id_len == 2) {
73                 switch (*((ushort *)comp_type_id)) {
74                         case MAKE_ID2('I', '8'): { comp_type = GWN_COMP_I8; goto success; }
75                         case MAKE_ID2('U', '8'): { comp_type = GWN_COMP_U8; goto success; }
76                 }
77         }
78         else if (comp_type_id_len == 3) {
79                 switch (*((uint *)comp_type_id)) {
80                         case MAKE_ID3('I', '1', '6'): { comp_type = GWN_COMP_I16; goto success; }
81                         case MAKE_ID3('U', '1', '6'): { comp_type = GWN_COMP_U16; goto success; }
82                         case MAKE_ID3('I', '3', '2'): { comp_type = GWN_COMP_I32; goto success; }
83                         case MAKE_ID3('U', '3', '2'): { comp_type = GWN_COMP_U32; goto success; }
84                         case MAKE_ID3('F', '3', '2'): { comp_type = GWN_COMP_F32; goto success; }
85                         case MAKE_ID3('I', '1', '0'): { comp_type = GWN_COMP_I10; goto success; }
86                 }
87         }
88
89         PyErr_Format(PyExc_ValueError,
90                      "unknown type literal: '%s'",
91                      comp_type_id);
92         return 0;
93
94 success:
95         *((Gwn_VertCompType *)p) = comp_type;
96         return 1;
97 }
98
99 static int BPy_Gwn_ParseVertFetchMode(PyObject *o, void *p)
100 {
101         Py_ssize_t mode_id_len;
102         const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len);
103         if (mode_id == NULL) {
104                 PyErr_Format(PyExc_ValueError,
105                              "expected a string, got %s",
106                              Py_TYPE(o)->tp_name);
107                 return 0;
108         }
109 #define MATCH_ID(id) \
110         if (mode_id_len == strlen(STRINGIFY(id))) { \
111                 if (STREQ(mode_id, STRINGIFY(id))) { \
112                         mode = GWN_FETCH_##id; \
113                         goto success; \
114                 } \
115         } ((void)0)
116
117         Gwn_VertCompType mode;
118         MATCH_ID(FLOAT);
119         MATCH_ID(INT);
120         MATCH_ID(INT_TO_FLOAT_UNIT);
121         MATCH_ID(INT_TO_FLOAT);
122 #undef MATCH_ID
123         PyErr_Format(PyExc_ValueError,
124                      "unknown type literal: '%s'",
125                      mode_id);
126         return 0;
127
128 success:
129         (*(Gwn_VertFetchMode *)p) = mode;
130         return 1;
131 }
132
133 static int BPy_Gwn_ParsePrimType(PyObject *o, void *p)
134 {
135         Py_ssize_t mode_id_len;
136         const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len);
137         if (mode_id == NULL) {
138                 PyErr_Format(PyExc_ValueError,
139                              "expected a string, got %s",
140                              Py_TYPE(o)->tp_name);
141                 return 0;
142         }
143 #define MATCH_ID(id) \
144         if (mode_id_len == strlen(STRINGIFY(id))) { \
145                 if (STREQ(mode_id, STRINGIFY(id))) { \
146                         mode = GWN_PRIM_##id; \
147                         goto success; \
148                 } \
149         } ((void)0)
150
151         Gwn_PrimType mode;
152         MATCH_ID(POINTS);
153         MATCH_ID(LINES);
154         MATCH_ID(TRIS);
155         MATCH_ID(LINE_STRIP);
156         MATCH_ID(LINE_LOOP);
157         MATCH_ID(TRI_STRIP);
158         MATCH_ID(TRI_FAN);
159         MATCH_ID(LINE_STRIP_ADJ);
160
161 #undef MATCH_ID
162         PyErr_Format(PyExc_ValueError,
163                      "unknown type literal: '%s'",
164                      mode_id);
165         return 0;
166
167 success:
168         (*(Gwn_VertFetchMode *)p) = mode;
169         return 1;
170 }
171
172 /** \} */
173
174
175 /* -------------------------------------------------------------------- */
176
177 /** \name Utility Functions
178  * \{ */
179
180 /* Use for both tuple and single item, TODO: GWN_COMP_I10 */
181 #define PY_AS_NATIVE_SWITCH \
182         switch (attr->comp_type) { \
183                 case GWN_COMP_I8:  { PY_AS_NATIVE(int8_t,   PyLong_AsLong); break; } \
184                 case GWN_COMP_U8:  { PY_AS_NATIVE(uint8_t,  PyLong_AsLong); break; } \
185                 case GWN_COMP_I16: { PY_AS_NATIVE(int16_t,  PyLong_AsLong); break; } \
186                 case GWN_COMP_U16: { PY_AS_NATIVE(uint16_t, PyLong_AsLong); break; } \
187                 case GWN_COMP_I32: { PY_AS_NATIVE(int32_t,  PyLong_AsLong); break; } \
188                 case GWN_COMP_U32: { PY_AS_NATIVE(uint32_t, PyLong_AsLong); break; } \
189                 case GWN_COMP_F32: { PY_AS_NATIVE(float, PyFloat_AsDouble); break; } \
190                 default: \
191                         BLI_assert(0); \
192         } ((void)0)
193
194 /* No error checking, callers must run PyErr_Occurred */
195 static void fill_format_elem(void *data_dst_void, PyObject *py_src, const Gwn_VertAttr *attr)
196 {
197         /* TODO: check overflow */
198
199 #define PY_AS_NATIVE(t, py_as_native) \
200         { \
201                 t *data_dst = data_dst_void; \
202                 *data_dst = py_as_native(py_src); \
203         } ((void)0)
204
205         PY_AS_NATIVE_SWITCH;
206
207 #undef PY_AS_NATIVE
208 }
209
210 /* No error checking, callers must run PyErr_Occurred */
211 static void fill_format_tuple(void *data_dst_void, PyObject *py_src, const Gwn_VertAttr *attr)
212 {
213         /* TODO: check overflow */
214         const uint len = attr->comp_ct;
215
216 #define PY_AS_NATIVE(t, py_as_native) \
217         t *data_dst = data_dst_void; \
218         for (uint i = 0; i < len; i++) { \
219                 data_dst[i] = py_as_native(PyTuple_GET_ITEM(py_src, i)); \
220         } ((void)0)
221
222         PY_AS_NATIVE_SWITCH;
223
224 #undef PY_AS_NATIVE
225 }
226
227 #undef PY_AS_NATIVE_SWITCH
228
229 static bool pygwn_vertbuf_fill_impl(
230         Gwn_VertBuf *vbo,
231         uint data_id, PyObject *seq)
232 {
233         bool ok = true;
234         const Gwn_VertAttr *attr = &vbo->format.attribs[data_id];
235
236         Gwn_VertBufRaw data_step;
237         GWN_vertbuf_attr_get_raw_data(vbo, data_id, &data_step);
238
239         PyObject *seq_fast = PySequence_Fast(seq, "Vertex buffer fill");
240         if (seq_fast == NULL) {
241                 goto finally;
242         }
243
244         const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
245
246         if (seq_len != vbo->vertex_ct) {
247                 PyErr_Format(PyExc_ValueError,
248                              "Expected a sequence of size %d, got %d",
249                              vbo->vertex_ct, seq_len);
250         }
251
252         PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
253
254         if (attr->comp_ct == 1) {
255                 for (uint i = 0; i < seq_len; i++) {
256                         uchar *data = (uchar *)GWN_vertbuf_raw_step(&data_step);
257                         PyObject *item = seq_items[i];
258                         fill_format_elem(data, item, attr);
259                 }
260         }
261         else {
262                 for (uint i = 0; i < seq_len; i++) {
263                         uchar *data = (uchar *)GWN_vertbuf_raw_step(&data_step);
264                         PyObject *item = seq_items[i];
265                         if (!PyTuple_CheckExact(item)) {
266                                 PyErr_Format(PyExc_ValueError,
267                                              "expected a tuple, got %s",
268                                              Py_TYPE(item)->tp_name);
269                                 ok = false;
270                                 goto finally;
271                         }
272                         if (PyTuple_GET_SIZE(item) != attr->comp_ct) {
273                                 PyErr_Format(PyExc_ValueError,
274                                              "expected a tuple of size %d, got %d",
275                                              attr->comp_ct, PyTuple_GET_SIZE(item));
276                                 ok = false;
277                                 goto finally;
278                         }
279
280                         /* May trigger error, check below */
281                         fill_format_tuple(data, item, attr);
282                 }
283         }
284
285         if (PyErr_Occurred()) {
286                 ok = false;
287         }
288
289 finally:
290
291         Py_DECREF(seq_fast);
292         return ok;
293 }
294
295 /* handy, but not used just now */
296 #if 0
297 static int pygwn_find_id(const Gwn_VertFormat *fmt, const char *id)
298 {
299         for (int i = 0; i < fmt->attrib_ct; i++) {
300                 for (uint j = 0; j < fmt->name_ct; j++) {
301                         if (STREQ(fmt->attribs[i].name[j], id)) {
302                                 return i;
303                         }
304                 }
305         }
306         return -1;
307 }
308 #endif
309
310 /** \} */
311
312
313 /* -------------------------------------------------------------------- */
314
315 /** \name VertFormat Type
316  * \{ */
317
318 static PyObject *BPy_Gwn_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
319 {
320         if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
321                 PyErr_SetString(PyExc_TypeError,
322                                 "VertFormat(): takes no arguments");
323                 return NULL;
324         }
325
326         BPy_Gwn_VertFormat *ret = (BPy_Gwn_VertFormat *)BPy_Gwn_VertFormat_CreatePyObject(NULL);
327
328         return (PyObject *)ret;
329 }
330
331 PyDoc_STRVAR(BPy_Gwn_VertFormat_attr_add_doc,
332 "TODO"
333 );
334 static PyObject *BPy_Gwn_VertFormat_attr_add(BPy_Gwn_VertFormat *self, PyObject *args, PyObject *kwds)
335 {
336         static const char *kwlist[] = {"id", "comp_type", "len", "fetch_mode", NULL};
337
338         struct {
339                 const char *id;
340                 Gwn_VertCompType comp_type;
341                 uint len;
342                 Gwn_VertFetchMode fetch_mode;
343         } params;
344
345         if (self->fmt.attrib_ct == GWN_VERT_ATTR_MAX_LEN) {
346                 PyErr_SetString(PyExc_ValueError, "Maxumum attr reached " STRINGIFY(GWN_VERT_ATTR_MAX_LEN));
347                 return NULL;
348         }
349
350         if (!PyArg_ParseTupleAndKeywords(
351                 args, kwds, "$sO&IO&:attr_add", (char **)kwlist,
352                 &params.id,
353                 BPy_Gwn_ParseVertCompType, &params.comp_type,
354                 &params.len,
355                 BPy_Gwn_ParseVertFetchMode, &params.fetch_mode))
356         {
357                 return NULL;
358         }
359
360         uint attr_id = GWN_vertformat_attr_add(&self->fmt, params.id, params.comp_type, params.len, params.fetch_mode);
361         return PyLong_FromLong(attr_id);
362 }
363
364 static struct PyMethodDef BPy_Gwn_VertFormat_methods[] = {
365         {"attr_add", (PyCFunction) BPy_Gwn_VertFormat_attr_add,
366          METH_VARARGS | METH_KEYWORDS, BPy_Gwn_VertFormat_attr_add_doc},
367         {NULL, NULL, 0, NULL}
368 };
369
370
371 static void BPy_Gwn_VertFormat_dealloc(BPy_Gwn_VertFormat *self)
372 {
373         Py_TYPE(self)->tp_free(self);
374 }
375
376 PyTypeObject BPy_Gwn_VertFormat_Type = {
377         PyVarObject_HEAD_INIT(NULL, 0)
378         .tp_name = "Gwn_VertFormat",
379         .tp_basicsize = sizeof(BPy_Gwn_VertFormat),
380         .tp_dealloc = (destructor)BPy_Gwn_VertFormat_dealloc,
381         .tp_flags = Py_TPFLAGS_DEFAULT,
382         .tp_methods = BPy_Gwn_VertFormat_methods,
383         .tp_new = BPy_Gwn_VertFormat_new,
384 };
385
386 /** \} */
387
388
389 /* -------------------------------------------------------------------- */
390
391 /** \name VertBuf Type
392  * \{ */
393
394 static PyObject *BPy_Gwn_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
395 {
396         const char * const keywords[] = {"len", "format", NULL};
397
398         struct {
399                 BPy_Gwn_VertFormat *py_fmt;
400                 uint len;
401         } params;
402
403         if (!PyArg_ParseTupleAndKeywords(
404                 args, kwds,
405                 "$IO!:Gwn_VertBuf.__new__", (char **)keywords,
406                 &params.len,
407                 &BPy_Gwn_VertFormat_Type, &params.py_fmt))
408         {
409                 return NULL;
410         }
411
412         struct Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&params.py_fmt->fmt);
413
414         GWN_vertbuf_data_alloc(vbo, params.len);
415
416         return BPy_Gwn_VertBuf_CreatePyObject(vbo);
417 }
418
419 PyDoc_STRVAR(BPy_Gwn_VertBuf_fill_doc,
420 "TODO"
421 );
422 static PyObject *BPy_Gwn_VertBuf_fill(BPy_Gwn_VertBuf *self, PyObject *args, PyObject *kwds)
423 {
424         static const char *kwlist[] = {"id", "data", NULL};
425
426         struct {
427                 uint id;
428                 PyObject *py_seq_data;
429         } params;
430
431         if (!PyArg_ParseTupleAndKeywords(
432                 args, kwds, "$IO:fill", (char **)kwlist,
433                 &params.id,
434                 &params.py_seq_data))
435         {
436                 return NULL;
437         }
438
439         if (params.id >= self->buf->format.attrib_ct) {
440                 PyErr_Format(PyExc_ValueError,
441                              "Format id '%s' out of range",
442                              params.id);
443         }
444
445         if (!pygwn_vertbuf_fill_impl(self->buf, params.id, params.py_seq_data)) {
446                 return NULL;
447         }
448         Py_RETURN_NONE;
449 }
450
451 static struct PyMethodDef BPy_Gwn_VertBuf_methods[] = {
452         {"fill", (PyCFunction) BPy_Gwn_VertBuf_fill,
453          METH_VARARGS | METH_KEYWORDS, BPy_Gwn_VertBuf_fill_doc},
454         {NULL, NULL, 0, NULL}
455 };
456
457 static void BPy_Gwn_VertBuf_dealloc(BPy_Gwn_VertBuf *self)
458 {
459         GWN_vertbuf_discard(self->buf);
460         Py_TYPE(self)->tp_free(self);
461 }
462
463 PyTypeObject BPy_Gwn_VertBuf_Type = {
464         PyVarObject_HEAD_INIT(NULL, 0)
465         .tp_name = "Gwn_VertBuf",
466         .tp_basicsize = sizeof(BPy_Gwn_VertBuf),
467         .tp_dealloc = (destructor)BPy_Gwn_VertBuf_dealloc,
468         .tp_flags = Py_TPFLAGS_DEFAULT,
469         .tp_methods = BPy_Gwn_VertBuf_methods,
470         .tp_new = BPy_Gwn_VertBuf_new,
471 };
472
473 /** \} */
474
475
476 /* -------------------------------------------------------------------- */
477
478 /** \name VertBatch Type
479  * \{ */
480
481 static PyObject *BPy_Gwn_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
482 {
483         const char * const keywords[] = {"type", "buf", NULL};
484
485         struct {
486                 Gwn_PrimType type_id;
487                 BPy_Gwn_VertBuf *py_buf;
488         } params;
489
490         if (!PyArg_ParseTupleAndKeywords(
491                 args, kwds,
492                 "$O&O!:Gwn_Batch.__new__", (char **)keywords,
493                 BPy_Gwn_ParsePrimType, &params.type_id,
494                 &BPy_Gwn_VertBuf_Type, &params.py_buf))
495         {
496                 return NULL;
497         }
498
499         Gwn_Batch *batch = GWN_batch_create(params.type_id, params.py_buf->buf, NULL);
500         BPy_Gwn_Batch *ret = (BPy_Gwn_Batch *)BPy_Gwn_Batch_CreatePyObject(batch);
501
502 #ifdef USE_GWN_PY_REFERENCES
503         ret->references = PyList_New(1);
504         PyList_SET_ITEM(ret->references, 0, (PyObject *)params.py_buf);
505         Py_INCREF(params.py_buf);
506         PyObject_GC_Track(ret);
507 #endif
508
509         return (PyObject *)ret;
510 }
511
512 PyDoc_STRVAR(BPy_Gwn_VertBatch_vertbuf_add_doc,
513 "TODO"
514 );
515 static PyObject *BPy_Gwn_VertBatch_vertbuf_add(BPy_Gwn_Batch *self, BPy_Gwn_VertBuf *py_buf)
516 {
517         if (!BPy_Gwn_VertBuf_Check(py_buf)) {
518                 PyErr_Format(PyExc_TypeError,
519                              "Expected a Gwn_VertBuf, got %s",
520                              Py_TYPE(py_buf)->tp_name);
521                 return NULL;
522         }
523
524         if (self->batch->verts[0]->vertex_ct != py_buf->buf->vertex_ct) {
525                 PyErr_Format(PyExc_TypeError,
526                              "Expected %d length, got %d",
527                              self->batch->verts[0]->vertex_ct, py_buf->buf->vertex_ct);
528                 return NULL;
529         }
530
531 #ifdef USE_GWN_PY_REFERENCES
532         /* Hold user */
533         PyList_Append(self->references, (PyObject *)py_buf);
534 #endif
535
536         GWN_batch_vertbuf_add(self->batch, py_buf->buf);
537         Py_RETURN_NONE;
538 }
539
540 /* Currently magic number from Py perspective. */
541 PyDoc_STRVAR(BPy_Gwn_VertBatch_program_set_builtin_doc,
542 "TODO"
543 );
544 static PyObject *BPy_Gwn_VertBatch_program_set_builtin(BPy_Gwn_Batch *self, PyObject *args, PyObject *kwds)
545 {
546         static const char *kwlist[] = {"id", NULL};
547
548         struct {
549                 const char *shader;
550         } params;
551
552         if (!PyArg_ParseTupleAndKeywords(
553                 args, kwds, "s:program_set_builtin", (char **)kwlist,
554                 &params.shader))
555         {
556                 return NULL;
557         }
558
559         GPUBuiltinShader shader;
560
561 #define MATCH_ID(id) \
562         if (STREQ(params.shader, STRINGIFY(id))) { \
563                 shader = GPU_SHADER_##id; \
564                 goto success; \
565         } ((void)0)
566
567         MATCH_ID(2D_FLAT_COLOR);
568         MATCH_ID(2D_SMOOTH_COLOR);
569         MATCH_ID(2D_UNIFORM_COLOR);
570
571         MATCH_ID(3D_FLAT_COLOR);
572         MATCH_ID(3D_SMOOTH_COLOR);
573         MATCH_ID(3D_UNIFORM_COLOR);
574
575 #undef MATCH_ID
576
577         PyErr_SetString(PyExc_ValueError,
578                         "shader name not known");
579         return NULL;
580
581 success:
582         GWN_batch_program_set_builtin(self->batch, shader);
583         Py_RETURN_NONE;
584 }
585
586 static PyObject *BPy_Gwn_VertBatch_uniform_bool(BPy_Gwn_Batch *self, PyObject *args)
587 {
588         struct {
589                 const char *id;
590                 bool values[1];
591         } params;
592
593         if (!PyArg_ParseTuple(
594                 args, "si:uniform_b",
595                 &params.id,
596                 &params.values[0]))
597         {
598                 return NULL;
599         }
600
601         GWN_batch_uniform_1b(self->batch, params.id, params.values[0]);
602         Py_RETURN_NONE;
603 }
604
605 static PyObject *BPy_Gwn_VertBatch_uniform_i32(BPy_Gwn_Batch *self, PyObject *args)
606 {
607         struct {
608                 const char *id;
609                 int values[1];
610         } params;
611
612         if (!PyArg_ParseTuple(
613                 args, "si:uniform_i",
614                 &params.id,
615                 &params.values[0]))
616         {
617                 return NULL;
618         }
619
620         GWN_batch_uniform_1i(self->batch, params.id, params.values[0]);
621         Py_RETURN_NONE;
622 }
623
624 static PyObject *BPy_Gwn_VertBatch_uniform_f32(BPy_Gwn_Batch *self, PyObject *args)
625 {
626         struct {
627                 const char *id;
628                 float values[4];
629         } params;
630
631         if (!PyArg_ParseTuple(
632                 args, "sf|fff:uniform_f",
633                 &params.id,
634                 &params.values[0], &params.values[1], &params.values[2], &params.values[3]))
635         {
636                 return NULL;
637         }
638
639         switch(PyTuple_GET_SIZE(args)) {
640                 case 2: GWN_batch_uniform_1f(self->batch,  params.id,  params.values[0]); break;
641                 case 3: GWN_batch_uniform_2fv(self->batch, params.id, params.values);     break;
642                 case 4: GWN_batch_uniform_3fv(self->batch, params.id, params.values);     break;
643                 case 5: GWN_batch_uniform_4fv(self->batch, params.id, params.values);     break;
644                 default:
645                         BLI_assert(0);
646         }
647         Py_RETURN_NONE;
648 }
649
650 PyDoc_STRVAR(BPy_Gwn_VertBatch_draw_doc,
651 "TODO"
652 );
653 static PyObject *BPy_Gwn_VertBatch_draw(BPy_Gwn_Batch *self)
654 {
655         if (!glIsProgram(self->batch->program)) {
656                 PyErr_SetString(PyExc_ValueError,
657                                 "batch program has not not set");
658         }
659         GWN_batch_draw(self->batch);
660         Py_RETURN_NONE;
661 }
662
663 static struct PyMethodDef BPy_Gwn_VertBatch_methods[] = {
664         {"vertbuf_add", (PyCFunction) BPy_Gwn_VertBatch_vertbuf_add,
665          METH_O, BPy_Gwn_VertBatch_vertbuf_add_doc},
666         {"program_set_builtin", (PyCFunction) BPy_Gwn_VertBatch_program_set_builtin,
667          METH_VARARGS | METH_KEYWORDS, BPy_Gwn_VertBatch_program_set_builtin_doc},
668         {"uniform_bool", (PyCFunction) BPy_Gwn_VertBatch_uniform_bool,
669          METH_VARARGS, NULL},
670         {"uniform_i32", (PyCFunction) BPy_Gwn_VertBatch_uniform_i32,
671          METH_VARARGS, NULL},
672         {"uniform_f32", (PyCFunction) BPy_Gwn_VertBatch_uniform_f32,
673           METH_VARARGS, NULL},
674         {"draw", (PyCFunction) BPy_Gwn_VertBatch_draw,
675          METH_NOARGS, BPy_Gwn_VertBatch_draw_doc},
676         {NULL, NULL, 0, NULL}
677 };
678
679 #ifdef USE_GWN_PY_REFERENCES
680
681 static int BPy_Gwn_Batch_traverse(BPy_Gwn_Batch *self, visitproc visit, void *arg)
682 {
683         Py_VISIT(self->references);
684         return 0;
685 }
686
687 static int BPy_Gwn_Batch_clear(BPy_Gwn_Batch *self)
688 {
689         Py_CLEAR(self->references);
690         return 0;
691 }
692
693 #endif
694
695 static void BPy_Gwn_Batch_dealloc(BPy_Gwn_Batch *self)
696 {
697         GWN_batch_discard(self->batch);
698
699 #ifdef USE_GWN_PY_REFERENCES
700         if (self->references) {
701                 PyObject_GC_UnTrack(self);
702                 BPy_Gwn_Batch_clear(self);
703                 Py_XDECREF(self->references);
704         }
705 #endif
706
707         Py_TYPE(self)->tp_free(self);
708 }
709
710 PyTypeObject BPy_Gwn_Batch_Type = {
711         PyVarObject_HEAD_INIT(NULL, 0)
712         .tp_name = "Gwn_Batch",
713         .tp_basicsize = sizeof(BPy_Gwn_Batch),
714         .tp_dealloc = (destructor)BPy_Gwn_Batch_dealloc,
715 #ifdef USE_GWN_PY_REFERENCES
716         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
717         .tp_traverse = (traverseproc)BPy_Gwn_Batch_traverse,
718         .tp_clear = (inquiry)BPy_Gwn_Batch_clear,
719 #else
720         .tp_flags = Py_TPFLAGS_DEFAULT,
721 #endif
722         .tp_methods = BPy_Gwn_VertBatch_methods,
723         .tp_new = BPy_Gwn_Batch_new,
724 };
725
726 /* -------------------------------------------------------------------- */
727
728
729 /** \name Gawain Types Module
730  * \{ */
731
732 static struct PyModuleDef BPy_BM_types_module_def = {
733         PyModuleDef_HEAD_INIT,
734         .m_name = "_gawain.types",
735 };
736
737 PyObject *BPyInit_gawain_types(void)
738 {
739         PyObject *submodule;
740
741         submodule = PyModule_Create(&BPy_BM_types_module_def);
742
743         if (PyType_Ready(&BPy_Gwn_VertFormat_Type) < 0)
744                 return NULL;
745         if (PyType_Ready(&BPy_Gwn_VertBuf_Type) < 0)
746                 return NULL;
747         if (PyType_Ready(&BPy_Gwn_Batch_Type) < 0)
748                 return NULL;
749
750 #define MODULE_TYPE_ADD(s, t) \
751         PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t)
752
753         MODULE_TYPE_ADD(submodule, BPy_Gwn_VertFormat_Type);
754         MODULE_TYPE_ADD(submodule, BPy_Gwn_VertBuf_Type);
755         MODULE_TYPE_ADD(submodule, BPy_Gwn_Batch_Type);
756
757 #undef MODULE_TYPE_ADD
758
759         return submodule;
760 }
761
762 /** \} */
763
764
765 /* -------------------------------------------------------------------- */
766
767 /** \name Public API
768  * \{ */
769
770 PyObject *BPy_Gwn_VertFormat_CreatePyObject(Gwn_VertFormat *fmt)
771 {
772         BPy_Gwn_VertFormat *self;
773
774         self = PyObject_New(BPy_Gwn_VertFormat, &BPy_Gwn_VertFormat_Type);
775         if (fmt) {
776                 self->fmt = *fmt;
777         }
778         else {
779                 memset(&self->fmt, 0, sizeof(self->fmt));
780         }
781
782         return (PyObject *)self;
783 }
784
785 PyObject *BPy_Gwn_VertBuf_CreatePyObject(Gwn_VertBuf *buf)
786 {
787         BPy_Gwn_VertBuf *self;
788
789         self = PyObject_New(BPy_Gwn_VertBuf, &BPy_Gwn_VertBuf_Type);
790         self->buf = buf;
791
792         return (PyObject *)self;
793 }
794
795
796 PyObject *BPy_Gwn_Batch_CreatePyObject(Gwn_Batch *batch)
797 {
798         BPy_Gwn_Batch *self;
799
800 #ifdef USE_GWN_PY_REFERENCES
801         self = (BPy_Gwn_Batch *)_PyObject_GC_New(&BPy_Gwn_Batch_Type);
802         self->references = NULL;
803 #else
804         self = PyObject_New(BPy_Gwn_Batch, &BPy_Gwn_Batch_Type);
805 #endif
806
807         self->batch = batch;
808
809         return (PyObject *)self;
810 }
811
812 /** \} */