From 564d37c4b67af534b6c12d2bebbd7883c3d3817c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 5 Oct 2018 15:10:56 +0200 Subject: Python API: new GPUVertFormat constructor and vbo.fill_attribute method Reviewer: fclem Differential Revision: https://developer.blender.org/D3760 --- source/blender/python/gpu/gpu_py_vertex_buffer.c | 98 +++++++----- source/blender/python/gpu/gpu_py_vertex_format.c | 180 ++++++++++++++++------- 2 files changed, 190 insertions(+), 88 deletions(-) (limited to 'source/blender/python') diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index 53fbbf623fa..fd4ee64c996 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -188,20 +188,28 @@ finally: return ok; } -/* handy, but not used just now */ -#if 0 -static int bpygpu_find_id(const GPUVertFormat *fmt, const char *id) +static int bpygpu_fill_attribute(GPUVertBuf *buf, int id, PyObject *py_seq_data) { - for (int i = 0; i < fmt->attr_len; i++) { - for (uint j = 0; j < fmt->name_len; j++) { - if (STREQ(fmt->attribs[i].name[j], id)) { - return i; - } - } + if (id < 0 || id >= buf->format.attr_len) { + PyErr_Format(PyExc_ValueError, + "Format id %d out of range", + id); + return 0; + } + + if (buf->data == NULL) { + PyErr_SetString(PyExc_ValueError, + "Can't fill, static buffer already in use"); + return 0; } - return -1; + + if (!bpygpu_vertbuf_fill_impl(buf, (uint)id, py_seq_data)) { + return 0; + } + + return 1; } -#endif + /** \} */ @@ -218,12 +226,12 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, uint len; } params; - static const char *_keywords[] = {"len", "format", NULL}; - static _PyArg_Parser _parser = {"$IO!:GPUVertBuf.__new__", _keywords, 0}; + static const char *_keywords[] = {"format", "len", NULL}; + static _PyArg_Parser _parser = {"O!I:GPUVertBuf.__new__", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast( args, kwds, &_parser, - ¶ms.len, - &BPyGPUVertFormat_Type, ¶ms.py_fmt)) + &BPyGPUVertFormat_Type, ¶ms.py_fmt, + ¶ms.len)) { return NULL; } @@ -235,48 +243,62 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, return BPyGPUVertBuf_CreatePyObject(vbo); } -PyDoc_STRVAR(bpygpu_VertBuf_fill_doc, -"TODO" +PyDoc_STRVAR(bpygpu_VertBuf_fill_attribute_doc, +"fill_attribute(identifier, data)\n" +"\n" +" Insert data into the buffer for a single attribute.\n" +"\n" +" :param identifier: Either the name or the id of the attribute.\n" +" :type identifier: int or str\n" +" :param data: Sequence of data that should be stored in the buffer\n" +" :type data: sequence of individual values or tuples\n" ); -static PyObject *bpygpu_VertBuf_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds) +static PyObject *bpygpu_VertBuf_fill_attribute(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds) { - struct { - uint id; - PyObject *py_seq_data; - } params; + PyObject *data; + PyObject *identifier; - static const char *_keywords[] = {"id", "data", NULL}; - static _PyArg_Parser _parser = {"$IO:fill", _keywords, 0}; + static const char *_keywords[] = {"identifier", "data", NULL}; + static _PyArg_Parser _parser = {"OO:fill_attribute", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast( args, kwds, &_parser, - ¶ms.id, - ¶ms.py_seq_data)) + &identifier, &data)) { return NULL; } - if (params.id >= self->buf->format.attr_len) { - PyErr_Format(PyExc_ValueError, - "Format id %d out of range", - params.id); - return NULL; - } + int id; - if (self->buf->data == NULL) { - PyErr_SetString(PyExc_ValueError, - "Can't fill, static buffer already in use"); + if (PyLong_Check(identifier)) { + id = PyLong_AsLong(identifier); + } + else if (PyUnicode_Check(identifier)) { + const char *name = PyUnicode_AsUTF8(identifier); + id = GPU_vertformat_attr_id_get(&self->buf->format, name); + if (id == -1) { + PyErr_SetString(PyExc_ValueError, + "Unknown attribute name"); + return NULL; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "expected int or str type as identifier"); return NULL; } - if (!bpygpu_vertbuf_fill_impl(self->buf, params.id, params.py_seq_data)) { + + if (!bpygpu_fill_attribute(self->buf, id, data)) { return NULL; } + Py_RETURN_NONE; } + static struct PyMethodDef bpygpu_VertBuf_methods[] = { - {"fill", (PyCFunction) bpygpu_VertBuf_fill, - METH_VARARGS | METH_KEYWORDS, bpygpu_VertBuf_fill_doc}, + {"fill_attribute", (PyCFunction) bpygpu_VertBuf_fill_attribute, + METH_VARARGS | METH_KEYWORDS, bpygpu_VertBuf_fill_attribute_doc}, {NULL, NULL, 0, NULL} }; diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c index 34f6af75477..794167fab7a 100644 --- a/source/blender/python/gpu/gpu_py_vertex_format.c +++ b/source/blender/python/gpu/gpu_py_vertex_format.c @@ -55,79 +55,104 @@ * Use with PyArg_ParseTuple's "O&" formatting. * \{ */ +static int bpygpu_parse_component_type(const char *str, int length) +{ + if (length == 2) { + switch (*((ushort *)str)) { + case MAKE_ID2('I', '8'): return GPU_COMP_I8; + case MAKE_ID2('U', '8'): return GPU_COMP_U8; + default: break; + } + } + else if (length == 3) { + switch (*((uint *)str)) { + case MAKE_ID3('I', '1', '6'): return GPU_COMP_I16; + case MAKE_ID3('U', '1', '6'): return GPU_COMP_U16; + case MAKE_ID3('I', '3', '2'): return GPU_COMP_I32; + case MAKE_ID3('U', '3', '2'): return GPU_COMP_U32; + case MAKE_ID3('F', '3', '2'): return GPU_COMP_F32; + case MAKE_ID3('I', '1', '0'): return GPU_COMP_I10; + default: break; + } + } + return -1; +} + +static int bpygpu_parse_fetch_mode(const char *str, int length) +{ +#define MATCH_ID(id) \ + if (length == strlen(STRINGIFY(id))) { \ + if (STREQ(str, STRINGIFY(id))) { \ + return GPU_FETCH_##id; \ + } \ + } ((void)0) + + MATCH_ID(FLOAT); + MATCH_ID(INT); + MATCH_ID(INT_TO_FLOAT_UNIT); + MATCH_ID(INT_TO_FLOAT); +#undef MATCH_ID + + return -1; +} + static int bpygpu_ParseVertCompType(PyObject *o, void *p) { - Py_ssize_t comp_type_id_len; - const char *comp_type_id = _PyUnicode_AsStringAndSize(o, &comp_type_id_len); - if (comp_type_id == NULL) { + Py_ssize_t length; + const char *str = _PyUnicode_AsStringAndSize(o, &length); + + if (str == NULL) { PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name); return 0; } - GPUVertCompType comp_type; - if (comp_type_id_len == 2) { - switch (*((ushort *)comp_type_id)) { - case MAKE_ID2('I', '8'): { comp_type = GPU_COMP_I8; goto success; } - case MAKE_ID2('U', '8'): { comp_type = GPU_COMP_U8; goto success; } - } - } - else if (comp_type_id_len == 3) { - switch (*((uint *)comp_type_id)) { - case MAKE_ID3('I', '1', '6'): { comp_type = GPU_COMP_I16; goto success; } - case MAKE_ID3('U', '1', '6'): { comp_type = GPU_COMP_U16; goto success; } - case MAKE_ID3('I', '3', '2'): { comp_type = GPU_COMP_I32; goto success; } - case MAKE_ID3('U', '3', '2'): { comp_type = GPU_COMP_U32; goto success; } - case MAKE_ID3('F', '3', '2'): { comp_type = GPU_COMP_F32; goto success; } - case MAKE_ID3('I', '1', '0'): { comp_type = GPU_COMP_I10; goto success; } - } + int comp_type = bpygpu_parse_component_type(str, length); + if (comp_type == -1) { + PyErr_Format(PyExc_ValueError, + "unkown component type: '%s", + str); + return 0; } - PyErr_Format(PyExc_ValueError, - "unknown type literal: '%s'", - comp_type_id); - return 0; - -success: *((GPUVertCompType *)p) = comp_type; return 1; } static int bpygpu_ParseVertFetchMode(PyObject *o, void *p) { - Py_ssize_t mode_id_len; - const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len); - if (mode_id == NULL) { + Py_ssize_t length; + const char *str = _PyUnicode_AsStringAndSize(o, &length); + + if (str == NULL) { PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name); return 0; } -#define MATCH_ID(id) \ - if (mode_id_len == strlen(STRINGIFY(id))) { \ - if (STREQ(mode_id, STRINGIFY(id))) { \ - mode = GPU_FETCH_##id; \ - goto success; \ - } \ - } ((void)0) - GPUVertFetchMode mode; - MATCH_ID(FLOAT); - MATCH_ID(INT); - MATCH_ID(INT_TO_FLOAT_UNIT); - MATCH_ID(INT_TO_FLOAT); -#undef MATCH_ID - PyErr_Format(PyExc_ValueError, - "unknown type literal: '%s'", - mode_id); - return 0; + int fetch_mode = bpygpu_parse_fetch_mode(str, length); + if (fetch_mode == -1) { + PyErr_Format(PyExc_ValueError, + "unknown type literal: '%s'", + str); + return 0; + } -success: - (*(GPUVertFetchMode *)p) = mode; + (*(GPUVertFetchMode *)p) = fetch_mode; return 1; } +static int get_default_fetch_mode(GPUVertCompType type) +{ + switch (type) + { + case GPU_COMP_F32: return GPU_FETCH_FLOAT; + default: return -1; + } +} + /** \} */ @@ -136,16 +161,71 @@ success: /** \name VertFormat Type * \{ */ +static int add_attribute_simple(GPUVertFormat *format, char *name, GPUVertCompType comp_type, int length) +{ + if (length <= 0) { + PyErr_SetString(PyExc_ValueError, + "length of an attribute must greater than 0"); + return 0; + } + + int fetch_mode = get_default_fetch_mode(comp_type); + if (fetch_mode == -1) { + PyErr_SetString(PyExc_ValueError, + "no default fetch mode found"); + return 0; + } + + GPU_vertformat_attr_add(format, name, comp_type, length, fetch_mode); + return 1; +} + +static int add_attribute_from_tuple(GPUVertFormat *format, PyObject *data) +{ + char *name; + GPUVertCompType comp_type; + int length; + + if (!PyArg_ParseTuple(data, "sO&i", &name, bpygpu_ParseVertCompType, &comp_type, &length)) { + return 0; + } + + return add_attribute_simple(format, name, comp_type, length); +} + +static int insert_attributes_from_list(GPUVertFormat *format, PyObject *list) +{ + Py_ssize_t amount = PyList_Size(list); + + for (Py_ssize_t i = 0; i < amount; i++) { + PyObject *element = PyList_GET_ITEM(list, i); + if (!PyTuple_Check(element)) { + PyErr_SetString(PyExc_TypeError, "expected a list of tuples"); + return 0; + } + if (!add_attribute_from_tuple(format, element)) return 0; + } + + return 1; +} + static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { - if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) { - PyErr_SetString(PyExc_TypeError, - "VertFormat(): takes no arguments"); + + PyObject *format_list; + + static const char *keywords[] = {"format", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char**)keywords, &PyList_Type, &format_list)) { return NULL; } BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL); + if (!insert_attributes_from_list(&ret->fmt, format_list)) { + Py_DecRef((PyObject *)ret); + return NULL; + } + return (PyObject *)ret; } -- cgit v1.2.3