Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/python')
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c14
-rw-r--r--source/blender/python/generic/idprop_py_api.c578
-rw-r--r--source/blender/python/generic/idprop_py_api.h46
-rw-r--r--source/blender/python/gpu/CMakeLists.txt4
-rw-r--r--source/blender/python/gpu/gpu_py_api.c8
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c176
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.c148
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.h23
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.c158
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.h7
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c18
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c81
-rw-r--r--source/blender/python/gpu/gpu_py_platform.h23
-rw-r--r--source/blender/python/gpu/gpu_py_state.c16
-rw-r--r--source/blender/python/gpu/gpu_py_texture.c32
-rw-r--r--source/blender/python/gpu/gpu_py_texture.h3
-rw-r--r--source/blender/python/intern/CMakeLists.txt2
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c1
-rw-r--r--source/blender/python/intern/bpy_interface.c11
-rw-r--r--source/blender/python/intern/bpy_operator.c8
-rw-r--r--source/blender/python/intern/bpy_rna.c30
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c150
-rw-r--r--source/blender/python/intern/bpy_rna_operator.h31
-rw-r--r--source/blender/python/intern/bpy_rna_types_capi.c18
-rw-r--r--source/blender/python/mathutils/mathutils.c12
-rw-r--r--source/blender/python/mathutils/mathutils_Euler.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c146
-rw-r--r--source/blender/python/mathutils/mathutils_interpolate.c15
-rw-r--r--source/blender/python/mathutils/mathutils_kdtree.c2
29 files changed, 1464 insertions, 299 deletions
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index ebe6ed79578..f1a8d450ea5 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1087,7 +1087,7 @@ PyDoc_STRVAR(
"3.0.\n");
static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw)
{
- static const char *kwlist[] = {"object", "depsgraph", "deform", "cage", "face_normals", NULL};
+ static const char *kwlist[] = {"object", "depsgraph", "cage", "face_normals", NULL};
PyObject *py_object;
PyObject *py_depsgraph;
Object *ob, *ob_eval;
@@ -1095,7 +1095,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
struct Scene *scene_eval;
Mesh *me_eval;
BMesh *bm;
- bool use_deform = true;
bool use_cage = false;
bool use_fnorm = true;
const CustomData_MeshMasks data_masks = CD_MASK_BMESH;
@@ -1104,13 +1103,11 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "OO|O&O&O&:from_object",
+ "OO|O&O&:from_object",
(char **)kwlist,
&py_object,
&py_depsgraph,
PyC_ParseBool,
- &use_deform,
- PyC_ParseBool,
&use_cage,
PyC_ParseBool,
&use_fnorm) ||
@@ -1125,13 +1122,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- if (use_deform == false) {
- PyErr_WarnEx(PyExc_FutureWarning,
- "from_object(...): the deform parameter is deprecated, assumed to be True, and "
- "will be removed in version 3.0",
- 1);
- }
-
const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
scene_eval = DEG_get_evaluated_scene(depsgraph);
ob_eval = DEG_get_evaluated_object(depsgraph, ob);
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index fc7054f675a..9b6ca7fcec5 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -42,6 +42,18 @@ extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
extern PyObject *pyrna_id_CreatePyObject(ID *id);
extern bool pyrna_id_CheckPyObject(PyObject *obj);
+/* Currently there is no need to expose this publicly. */
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group);
+
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type);
+static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value);
+
/* -------------------------------------------------------------------- */
/** \name Python from ID-Property (Internal Conversions)
*
@@ -756,13 +768,7 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject
static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
{
- BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- Py_INCREF(self);
- iter->mode = IDPROP_ITER_KEYS;
- iter->cur = self->prop->data.group.first;
- PyObject_GC_Track(iter);
- return (PyObject *)iter;
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
/* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
@@ -875,6 +881,370 @@ PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name ID-Property Group Iterator Type
+ * \{ */
+
+static PyObject *BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self)
+{
+ if (self->len_init == self->group->prop->len) {
+ return true;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration");
+ return false;
+}
+
+static PyObject *BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return PyUnicode_FromString(cur->name);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEMS(ret,
+ PyUnicode_FromString(cur->name),
+ BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
+ return ret;
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group Iterator. */
+static void IDGroup_Iter_init_type(void)
+{
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupIterKeys";
+ v_ty->tp_name = "IDPropertyGroupIterValues";
+ i_ty->tp_name = "IDPropertyGroupIterItems";
+
+ k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next;
+ v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next;
+ i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next;
+
+ /* Shared members. */
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear);
+ SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter);
+
+#undef SHARED_MEMBER_SET
+}
+
+static PyObject *IDGroup_Iter_New_WithType(BPy_IDProperty *group,
+ const bool reversed,
+ PyTypeObject *type)
+{
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type);
+ iter->reversed = reversed;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
+ iter->cur = (reversed ? group->prop->data.group.last : group->prop->data.group.first);
+ iter->len_init = group->prop->len;
+ }
+ else {
+ iter->cur = NULL;
+ iter->len_init = 0;
+ }
+ return (PyObject *)iter;
+}
+
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type);
+}
+
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterValues_Type);
+}
+
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID-Property Group View Types (Keys/Values/Items)
+ *
+ * This view types is a thin wrapper on keys/values/items, this matches Python's `dict_view` type.
+ * The is returned by `property.keys()` and is separate from the iterator that loops over keys.
+ *
+ * There are some less common features this type could support (matching Python's `dict_view`)
+ *
+ * TODO:
+ * - Efficient contains checks for values and items which currently convert to a list first.
+ * - Missing `dict_views.isdisjoint`.
+ * - Missing `tp_as_number` (`nb_subtract`, `nb_and`, `nb_xor`, `nb_or`).
+ * \{ */
+
+static PyObject *BPy_IDGroup_View_repr(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+/* View Specific API's (Key/Value/Items). */
+
+static PyObject *BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewValues_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewItems_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed);
+}
+
+static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return self->group->prop->len;
+}
+
+static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return BPy_IDGroup_Contains(self->group, value);
+}
+
+static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewKeys_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewValues_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewItems_Contains, /* sq_contains */
+};
+
+/* Methods. */
+
+PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc,
+ "Return a reverse iterator over the ID Property keys values or items.");
+
+static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *UNUSED(ignored))
+{
+ BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self));
+ result->reversed = !self->reversed;
+ return (PyObject *)result;
+}
+
+static PyMethodDef BPy_IDGroup_View_methods[] = {
+ {"__reversed__",
+ (PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed,
+ METH_NOARGS,
+ BPy_IDGroup_View_reversed_doc},
+ {NULL, NULL},
+};
+
+PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group View. */
+static void IDGroup_View_init_type(void)
+{
+ PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupViewKeys";
+ v_ty->tp_name = "IDPropertyGroupViewValues";
+ i_ty->tp_name = "IDPropertyGroupViewItems";
+
+ k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter;
+ v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter;
+ i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter;
+
+ k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence;
+ v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence;
+ i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence;
+
+ /* Shared members. */
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear);
+ SHARED_MEMBER_SET(tp_methods, BPy_IDGroup_View_methods);
+
+#undef SHARED_MEMBER_SET
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name ID-Property Group Methods
* \{ */
@@ -923,22 +1293,6 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
return pyform;
}
-PyDoc_STRVAR(
- BPy_IDGroup_iter_items_doc,
- ".. method:: iteritems()\n"
- "\n"
- " Iterate through the items in the dict; behaves like dictionary method iteritems.\n");
-static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
-{
- BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- Py_INCREF(self);
- iter->mode = IDPROP_ITER_ITEMS;
- iter->cur = self->prop->data.group.first;
- PyObject_GC_Track(iter);
- return (PyObject *)iter;
-}
-
/* utility function */
static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
{
@@ -1023,13 +1377,37 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
return seq;
}
+PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewKeys_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewValues_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewItems_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
PyDoc_STRVAR(BPy_IDGroup_keys_doc,
".. method:: keys()\n"
"\n"
" Return the keys associated with this group as a list of strings.\n");
static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
{
- return BPy_Wrap_GetKeys(self->prop);
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_values_doc,
@@ -1038,16 +1416,16 @@ PyDoc_STRVAR(BPy_IDGroup_values_doc,
" Return the values associated with this group.\n");
static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
{
- return BPy_Wrap_GetValues(self->id, self->prop);
+ return BPy_IDGroup_ViewValues_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_items_doc,
".. method:: items()\n"
"\n"
- " Return the items associated with this group.\n");
+ " Iterate through the items in the dict; behaves like dictionary method items.\n");
static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
{
- return BPy_Wrap_GetItems(self->id, self->prop);
+ return BPy_IDGroup_ViewItems_CreatePyObject(self);
}
static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
@@ -1148,7 +1526,6 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
static struct PyMethodDef BPy_IDGroup_methods[] = {
{"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc},
- {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
{"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
{"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
{"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
@@ -1678,120 +2055,59 @@ PyTypeObject BPy_IDArray_Type = {
/** \} */
/* -------------------------------------------------------------------- */
-/** \name ID-Property Group Iterator Type
+/** \name Initialize Types
* \{ */
-static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+void IDProp_Init_Types(void)
{
- return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
-}
+ IDGroup_Iter_init_type();
+ IDGroup_View_init_type();
-static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
-{
- PyObject_GC_UnTrack(self);
- Py_CLEAR(self->group);
- PyObject_GC_Del(self);
+ PyType_Ready(&BPy_IDGroup_Type);
+ PyType_Ready(&BPy_IDArray_Type);
+
+ PyType_Ready(&BPy_IDGroup_IterKeys_Type);
+ PyType_Ready(&BPy_IDGroup_IterValues_Type);
+ PyType_Ready(&BPy_IDGroup_IterItems_Type);
+
+ PyType_Ready(&BPy_IDGroup_ViewKeys_Type);
+ PyType_Ready(&BPy_IDGroup_ViewValues_Type);
+ PyType_Ready(&BPy_IDGroup_ViewItems_Type);
}
-static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+/**
+ * \note `group` may be NULL, unlike most other uses of this argument.
+ * This is supported so RNA keys/values/items methods returns an iterator with the expected type:
+ * - Without having ID-properties.
+ * - Without supporting #BPy_IDProperty.prop being NULL, which would incur many more checks.
+ * Python's own dictionary-views also works this way too.
+ */
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type)
{
- Py_VISIT(self->group);
- return 0;
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type);
+ iter->reversed = false;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
+ }
+ return iter;
}
-static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group)
{
- Py_CLEAR(self->group);
- return 0;
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type);
}
-static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group)
{
-
- if (self->cur) {
- PyObject *ret;
- IDProperty *cur;
-
- cur = self->cur;
- self->cur = self->cur->next;
-
- if (self->mode == IDPROP_ITER_ITEMS) {
- ret = PyTuple_New(2);
- PyTuple_SET_ITEMS(ret,
- PyUnicode_FromString(cur->name),
- BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
- return ret;
- }
-
- return PyUnicode_FromString(cur->name);
- }
-
- PyErr_SetNone(PyExc_StopIteration);
- return NULL;
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type);
}
-PyTypeObject BPy_IDGroup_Iter_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- /* For printing, in format "<module>.<name>" */
- "IDPropertyGroupIter", /* char *tp_name; */
- sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */
- 0, /* tp_itemsize; For allocation */
-
- /* Methods to implement standard operations */
-
- (destructor)BPy_IDGroup_Iter_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- NULL, /* getattrfunc tp_getattr; */
- NULL, /* setattrfunc tp_setattr; */
- NULL, /* cmpfunc tp_compare; */
- (reprfunc)IDGroup_Iter_repr, /* reprfunc tp_repr; */
-
- /* Method suites for standard classes */
-
- NULL, /* PyNumberMethods *tp_as_number; */
- NULL, /* PySequenceMethods *tp_as_sequence; */
- NULL, /* PyMappingMethods *tp_as_mapping; */
-
- /* More standard operations (here for binary compatibility) */
-
- NULL, /* hashfunc tp_hash; */
- NULL, /* ternaryfunc tp_call; */
- NULL, /* reprfunc tp_str; */
- NULL, /* getattrofunc tp_getattro; */
- NULL, /* setattrofunc tp_setattro; */
-
- /* Functions to access object as input/output buffer */
- NULL, /* PyBufferProcs *tp_as_buffer; */
-
- /*** Flags to define presence of optional/expanded features ***/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* long tp_flags; */
-
- NULL, /* char *tp_doc; Documentation string */
- /*** Assigned meaning in release 2.0 ***/
- /* call function for all accessible objects */
- (traverseproc)BPy_IDGroup_Iter_traverse, /* traverseproc tp_traverse; */
-
- /* delete references to contained objects */
- (inquiry)BPy_IDGroup_Iter_clear, /* inquiry tp_clear; */
-
- /*** Assigned meaning in release 2.1 ***/
- /*** rich comparisons ***/
- NULL, /* richcmpfunc tp_richcompare; */
-
- /*** weak reference enabler ***/
- 0, /* long tp_weaklistoffset; */
-
- /*** Added in release 2.2 ***/
- /* Iterators */
- PyObject_SelfIter, /* getiterfunc tp_iter; */
- (iternextfunc)BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
-};
-
-void IDProp_Init_Types(void)
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group)
{
- PyType_Ready(&BPy_IDGroup_Type);
- PyType_Ready(&BPy_IDGroup_Iter_Type);
- PyType_Ready(&BPy_IDArray_Type);
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type);
}
/** \} */
@@ -1822,7 +2138,15 @@ static PyObject *BPyInit_idprop_types(void)
/* bmesh_py_types.c */
PyModule_AddType(submodule, &BPy_IDGroup_Type);
- PyModule_AddType(submodule, &BPy_IDGroup_Iter_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type);
+
PyModule_AddType(submodule, &BPy_IDArray_Type);
return submodule;
diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h
index 4cccea3a936..1e8e26a3b6d 100644
--- a/source/blender/python/generic/idprop_py_api.h
+++ b/source/blender/python/generic/idprop_py_api.h
@@ -25,16 +25,35 @@ struct ID;
struct IDProperty;
extern PyTypeObject BPy_IDArray_Type;
-extern PyTypeObject BPy_IDGroup_Iter_Type;
extern PyTypeObject BPy_IDGroup_Type;
+extern PyTypeObject BPy_IDGroup_ViewKeys_Type;
+extern PyTypeObject BPy_IDGroup_ViewValues_Type;
+extern PyTypeObject BPy_IDGroup_ViewItems_Type;
+
+extern PyTypeObject BPy_IDGroup_IterKeys_Type;
+extern PyTypeObject BPy_IDGroup_IterValues_Type;
+extern PyTypeObject BPy_IDGroup_IterItems_Type;
+
#define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type))
#define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type)
-#define BPy_IDGroup_Iter_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Iter_Type))
-#define BPy_IDGroup_Iter_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Iter_Type)
#define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type))
#define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type)
+#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type))
+#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type)
+#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type))
+#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type)
+#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type))
+#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type)
+
+#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type))
+#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type)
+#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type))
+#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type)
+#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type))
+#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type)
+
typedef struct BPy_IDProperty {
PyObject_VAR_HEAD
struct ID *id; /* can be NULL */
@@ -52,12 +71,28 @@ typedef struct BPy_IDGroup_Iter {
PyObject_VAR_HEAD
BPy_IDProperty *group;
struct IDProperty *cur;
- int mode;
+ /** Use for detecting manipulation during iteration (which is not allowed). */
+ int len_init;
+ /** Iterate in the reverse direction. */
+ bool reversed;
} BPy_IDGroup_Iter;
+/** Use to implement `IDPropertyGroup.keys/values/items` */
+typedef struct BPy_IDGroup_View {
+ PyObject_VAR_HEAD
+ /** This will be NULL when accessing keys on data that has no ID properties. */
+ BPy_IDProperty *group;
+ bool reversed;
+} BPy_IDGroup_View;
+
PyObject *BPy_Wrap_GetKeys(struct IDProperty *prop);
PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop);
PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop);
+
+PyObject *BPy_Wrap_GetKeys_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetValues_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetItems_View_WithID(struct ID *id, struct IDProperty *prop);
+
int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val);
PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop);
@@ -67,6 +102,3 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *grou
void IDProp_Init_Types(void);
PyObject *BPyInit_idprop(void);
-
-#define IDPROP_ITER_KEYS 0
-#define IDPROP_ITER_ITEMS 1
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index fe5c559fcc0..1424b35a004 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -37,10 +37,12 @@ set(SRC
gpu_py_api.c
gpu_py_batch.c
gpu_py_buffer.c
+ gpu_py_capabilities.c
gpu_py_element.c
gpu_py_framebuffer.c
gpu_py_matrix.c
gpu_py_offscreen.c
+ gpu_py_platform.c
gpu_py_select.c
gpu_py_shader.c
gpu_py_state.c
@@ -54,10 +56,12 @@ set(SRC
gpu_py_api.h
gpu_py_batch.h
gpu_py_buffer.h
+ gpu_py_capabilities.h
gpu_py_element.h
gpu_py_framebuffer.h
gpu_py_matrix.h
gpu_py_offscreen.h
+ gpu_py_platform.h
gpu_py_select.h
gpu_py_shader.h
gpu_py_state.h
diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c
index 0bc18e73d0c..5119b3612f8 100644
--- a/source/blender/python/gpu/gpu_py_api.c
+++ b/source/blender/python/gpu/gpu_py_api.c
@@ -30,7 +30,9 @@
#include "../generic/python_utildefines.h"
+#include "gpu_py_capabilities.h"
#include "gpu_py_matrix.h"
+#include "gpu_py_platform.h"
#include "gpu_py_select.h"
#include "gpu_py_state.h"
#include "gpu_py_types.h"
@@ -61,9 +63,15 @@ PyObject *BPyInit_gpu(void)
PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index d0965e83e33..e36e3b42617 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -37,17 +37,90 @@
#include "gpu_py_buffer.h"
-// #define PYGPU_BUFFER_PROTOCOL
+//#define PYGPU_BUFFER_PROTOCOL
+#define MAX_DIMENSIONS 64
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
-static bool pygpu_buffer_dimensions_compare(int ndim,
- const Py_ssize_t *shape_a,
- const Py_ssize_t *shape_b)
+static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
+{
+ Py_ssize_t tot = shape[0];
+ for (int i = 1; i < shape_len; i++) {
+ tot *= shape[i];
+ }
+
+ return tot;
+}
+
+static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
+ const Py_ssize_t shape_a_len,
+ const Py_ssize_t *shape_b,
+ const Py_ssize_t shape_b_len)
+{
+ if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
+ pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) {
+ PyErr_Format(PyExc_BufferError, "array size does not match");
+ return false;
+ }
+
+ return true;
+}
+
+static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
+ Py_ssize_t r_shape[MAX_DIMENSIONS],
+ Py_ssize_t *r_shape_len)
{
- return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t));
+ Py_ssize_t shape_len = 0;
+ if (PyLong_Check(shape_obj)) {
+ shape_len = 1;
+ if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ else if (PySequence_Check(shape_obj)) {
+ shape_len = PySequence_Size(shape_obj);
+ if (shape_len > MAX_DIMENSIONS) {
+ PyErr_SetString(PyExc_AttributeError,
+ "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
+ return false;
+ }
+ if (shape_len < 1) {
+ PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
+ return false;
+ }
+
+ for (int i = 0; i < shape_len; i++) {
+ PyObject *ob = PySequence_GetItem(shape_obj, i);
+ if (!PyLong_Check(ob)) {
+ PyErr_Format(PyExc_TypeError,
+ "invalid dimension %i, expected an int, not a %.200s",
+ i,
+ Py_TYPE(ob)->tp_name);
+ Py_DECREF(ob);
+ return false;
+ }
+
+ r_shape[i] = PyLong_AsLong(ob);
+ Py_DECREF(ob);
+
+ if (r_shape[i] < 1) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "invalid second argument argument expected a sequence "
+ "or an int, not a %.200s",
+ Py_TYPE(shape_obj)->tp_name);
+ }
+
+ *r_shape_len = shape_len;
+ return true;
}
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
@@ -174,7 +247,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
return list;
}
-static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
+static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg))
{
PyObject *list = PyList_New(self->shape_len);
int i;
@@ -186,6 +259,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
return list;
}
+static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type))
+{
+ Py_ssize_t shape[MAX_DIMENSIONS];
+ Py_ssize_t shape_len = 0;
+
+ if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
+ return -1;
+ }
+
+ if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
+ return -1;
+ }
+
+ size_t size = shape_len * sizeof(*self->shape);
+ if (shape_len != self->shape_len) {
+ MEM_freeN(self->shape);
+ self->shape = MEM_mallocN(size, __func__);
+ }
+
+ self->shape_len = shape_len;
+ memcpy(self->shape, shape, size);
+ return 0;
+}
+
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
{
Py_VISIT(self->parent);
@@ -280,14 +377,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
return err;
}
-#define MAX_DIMENSIONS 64
static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
PyObject *length_ob, *init = NULL;
BPyGPUBuffer *buffer = NULL;
Py_ssize_t shape[MAX_DIMENSIONS];
- Py_ssize_t i, shape_len = 0;
+ Py_ssize_t shape_len = 0;
if (kwds && PyDict_Size(kwds)) {
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
@@ -300,49 +396,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (PyLong_Check(length_ob)) {
- shape_len = 1;
- if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- else if (PySequence_Check(length_ob)) {
- shape_len = PySequence_Size(length_ob);
- if (shape_len > MAX_DIMENSIONS) {
- PyErr_SetString(PyExc_AttributeError,
- "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
- return NULL;
- }
- if (shape_len < 1) {
- PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
- return NULL;
- }
-
- for (i = 0; i < shape_len; i++) {
- PyObject *ob = PySequence_GetItem(length_ob, i);
- if (!PyLong_Check(ob)) {
- PyErr_Format(PyExc_TypeError,
- "invalid dimension %i, expected an int, not a %.200s",
- i,
- Py_TYPE(ob)->tp_name);
- Py_DECREF(ob);
- return NULL;
- }
- shape[i] = PyLong_AsLong(ob);
- Py_DECREF(ob);
-
- if (shape[i] < 1) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- }
- else {
- PyErr_Format(PyExc_TypeError,
- "invalid second argument argument expected a sequence "
- "or an int, not a %.200s",
- Py_TYPE(length_ob)->tp_name);
+ if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) {
return NULL;
}
@@ -354,11 +408,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (shape_len != pybuffer.ndim ||
- !pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) {
- PyErr_Format(PyExc_TypeError, "array size does not match");
- }
- else {
+ if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) {
buffer = pygpu_buffer_make_from_data(
init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf);
}
@@ -518,7 +568,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = {
};
static PyGetSetDef pygpu_buffer_getseters[] = {
- {"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL},
+ {"dimensions",
+ (getter)pygpu_buffer_dimensions_get,
+ (setter)pygpu_buffer_dimensions_set,
+ NULL,
+ NULL},
{NULL, NULL, NULL, NULL, NULL},
};
@@ -625,13 +679,7 @@ static size_t pygpu_buffer_calc_size(const int format,
const int shape_len,
const Py_ssize_t *shape)
{
- size_t r_size = GPU_texture_dataformat_size(format);
-
- for (int i = 0; i < shape_len; i++) {
- r_size *= shape[i];
- }
-
- return r_size;
+ return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format);
}
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c
new file mode 100644
index 00000000000..cedce485253
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.c
@@ -0,0 +1,148 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_capabilities.h"
+
+#include "gpu_py_capabilities.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_size());
+}
+
+static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_layers());
+}
+
+static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures());
+}
+
+static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_vert());
+}
+
+static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_geom());
+}
+
+static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_frag());
+}
+
+static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_vert());
+}
+
+static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_frag());
+}
+
+static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_indices());
+}
+
+static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_vertices());
+}
+
+static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_vertex_attribs());
+}
+
+static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_varying_floats());
+}
+
+static PyObject *pygpu_extensions_get(PyObject *UNUSED(self))
+{
+ int extensions_len = GPU_extensions_len();
+ PyObject *ret = PyTuple_New(extensions_len);
+ PyObject **ob_items = ((PyTupleObject *)ret)->ob_item;
+ for (int i = 0; i < extensions_len; i++) {
+ ob_items[i] = PyUnicode_FromString(GPU_extension_get(i));
+ }
+
+ return ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_capabilities__tp_methods[] = {
+ {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL},
+ {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL},
+ {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL},
+ {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL},
+ {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL},
+ {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL},
+ {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL},
+ {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL},
+ {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL},
+ {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL},
+ {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL},
+ {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL},
+ {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_capabilities__tp_doc, "This module provides access to the GPU capabilities.");
+static PyModuleDef pygpu_capabilities_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.capabilities",
+ .m_doc = pygpu_capabilities__tp_doc,
+ .m_methods = pygpu_capabilities__tp_methods,
+};
+
+PyObject *bpygpu_capabilities_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_capabilities_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_capabilities.h b/source/blender/python/gpu/gpu_py_capabilities.h
new file mode 100644
index 00000000000..ac138dda0c9
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_capabilities_init(void);
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c
index bc393aaafa4..7f64cb90a97 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.c
+++ b/source/blender/python/gpu/gpu_py_framebuffer.c
@@ -37,6 +37,8 @@
#include "gpu_py.h"
#include "gpu_py_texture.h"
+#include "gpu_py.h"
+#include "gpu_py_buffer.h"
#include "gpu_py_framebuffer.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -46,13 +48,7 @@
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
{
if (UNLIKELY(bpygpu_fb->fb == NULL)) {
- PyErr_SetString(PyExc_ReferenceError,
-#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
- "GPU framebuffer was freed, no further access is valid"
-#else
- "GPU framebuffer: internal error"
-#endif
- );
+ PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
return -1;
}
return 0;
@@ -68,10 +64,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
{
- if (!fb) {
- return;
- }
-
if (GPU_is_init()) {
GPU_framebuffer_free(fb);
}
@@ -80,6 +72,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
}
}
+static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
+{
+ if (self->fb) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_framebuffer_py_reference_set(self->fb, NULL);
+ if (!self->shared_reference)
+#endif
+ {
+ pygpu_framebuffer_free_if_possible(self->fb);
+ }
+
+ self->fb = NULL;
+ }
+}
+
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
@@ -336,7 +343,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
- return BPyGPUFrameBuffer_CreatePyObject(fb_python);
+ return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
}
PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc,
@@ -450,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U
return ret;
}
+PyDoc_STRVAR(
+ pygpu_framebuffer_read_color_doc,
+ ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
+ "\n"
+ " Read a block of pixels from the frame buffer.\n"
+ "\n"
+ " :param x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :param xsize, ysize: Dimensions of the pixel rectangle.\n"
+ " :type x, y, xsize, ysize: int\n"
+ " :param channels: Number of components to read.\n"
+ " :type channels: int\n"
+ " :param slot: The framebuffer slot to read data from.\n"
+ " :type slot: int\n"
+ " :param format: The format that describes the content of a single channel.\n"
+ " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
+ " :type type: str\n"
+ " :arg data: Optional Buffer object to fill with the pixels values.\n"
+ " :type data: :class:`gpu.types.Buffer`\n"
+ " :return: The Buffer with the read pixels.\n"
+ " :rtype: :class:`gpu.types.Buffer`\n");
+static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
+ int x, y, w, h, channels;
+ uint slot;
+ struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8};
+ BPyGPUBuffer *py_buffer = NULL;
+
+ static const char *_keywords[] = {
+ "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL};
+ static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &x,
+ &y,
+ &w,
+ &h,
+ &channels,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_dataformat,
+ &BPyGPU_BufferType,
+ &py_buffer)) {
+ return NULL;
+ }
+
+ if (!IN_RANGE_INCL(channels, 1, 4)) {
+ PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
+ return NULL;
+ }
+
+ if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
+ PyErr_SetString(PyExc_ValueError, "slot overflow");
+ return NULL;
+ }
+
+ if (py_buffer) {
+ if (pygpu_dataformat.value_found != py_buffer->format) {
+ PyErr_SetString(PyExc_AttributeError,
+ "the format of the buffer is different from that specified");
+ return NULL;
+ }
+
+ size_t size_curr = bpygpu_Buffer_size(py_buffer);
+ size_t size_expected = w * h * channels *
+ GPU_texture_dataformat_size(pygpu_dataformat.value_found);
+ if (size_curr < size_expected) {
+ PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
+ return NULL;
+ }
+ }
+ else {
+ py_buffer = BPyGPU_Buffer_CreatePyObject(
+ pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL);
+ BLI_assert(bpygpu_Buffer_size(py_buffer) ==
+ w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found));
+ }
+
+ GPU_framebuffer_read_color(self->fb,
+ x,
+ y,
+ w,
+ h,
+ channels,
+ (int)slot,
+ pygpu_dataformat.value_found,
+ py_buffer->buf.as_void);
+
+ return (PyObject *)py_buffer;
+}
+
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_framebuffer_free_doc,
".. method:: free()\n"
@@ -459,15 +560,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc,
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
{
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
- pygpu_framebuffer_free_if_possible(self->fb);
- self->fb = NULL;
+ pygpu_framebuffer_free_safe(self);
Py_RETURN_NONE;
}
#endif
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
{
- pygpu_framebuffer_free_if_possible(self->fb);
+ pygpu_framebuffer_free_safe(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
@@ -494,6 +594,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
(PyCFunction)pygpu_framebuffer_viewport_get,
METH_NOARGS,
pygpu_framebuffer_viewport_get_doc},
+ {"read_color",
+ (PyCFunction)pygpu_framebuffer_read_color,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_framebuffer_read_color_doc},
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
#endif
@@ -531,13 +635,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = {
/** \name Public API
* \{ */
-PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb)
+PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
{
BPyGPUFrameBuffer *self;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (shared_reference) {
+ void **ref = GPU_framebuffer_py_reference_get(fb);
+ if (ref) {
+ /* Retrieve BPyGPUFrameBuffer reference. */
+ self = (BPyGPUFrameBuffer *)POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb));
+ BLI_assert(self->fb == fb);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ }
+#else
+ UNUSED_VARS(shared_reference);
+#endif
+
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
self->fb = fb;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ self->shared_reference = shared_reference;
+
+ BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL);
+ GPU_framebuffer_py_reference_set(fb, (void **)&self->fb);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.h b/source/blender/python/gpu/gpu_py_framebuffer.h
index 7113e7c35aa..6727328518c 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.h
+++ b/source/blender/python/gpu/gpu_py_framebuffer.h
@@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type;
typedef struct BPyGPUFrameBuffer {
PyObject_HEAD struct GPUFrameBuffer *fb;
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ bool shared_reference;
+#endif
} BPyGPUFrameBuffer;
-PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1);
+PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 9e9a0b9066a..28bd24a6877 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -53,6 +53,8 @@
#include "../generic/py_capi_utils.h"
#include "gpu_py.h"
+#include "gpu_py_texture.h"
+
#include "gpu_py_offscreen.h" /* own include */
/* Define the free method to avoid breakage. */
@@ -264,6 +266,17 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *
return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
}
+PyDoc_STRVAR(pygpu_offscreen_texture_color_doc,
+ "The color texture attached.\n"
+ "\n"
+ ":type: :class:`gpu.types.GPUTexture`");
+static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
+ return BPyGPUTexture_CreatePyObject(texture, true);
+}
+
PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
@@ -385,6 +398,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
(setter)NULL,
pygpu_offscreen_color_texture_doc,
NULL},
+ {"texture_color",
+ (getter)pygpu_offscreen_texture_color_get,
+ (setter)NULL,
+ pygpu_offscreen_texture_color_doc,
+ NULL},
{"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
{"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
new file mode 100644
index 00000000000..e49ad18dfd8
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_platform.h"
+
+#include "gpu_py_platform.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_vendor());
+}
+
+static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_renderer());
+}
+
+static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_version());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_platform__tp_methods[] = {
+ {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL},
+ {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL},
+ {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_platform__tp_doc, "This module provides access to GPU Platform definitions.");
+static PyModuleDef pygpu_platform_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.platform",
+ .m_doc = pygpu_platform__tp_doc,
+ .m_methods = pygpu_platform__tp_methods,
+};
+
+PyObject *bpygpu_platform_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_platform_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_platform.h b/source/blender/python/gpu/gpu_py_platform.h
new file mode 100644
index 00000000000..19e3e41fb49
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_platform.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_platform_init(void);
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index 6b0fade8d1c..173c5afba56 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -25,11 +25,13 @@
#include <Python.h>
+#include "GPU_framebuffer.h"
#include "GPU_state.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
+#include "gpu_py_framebuffer.h"
#include "gpu_py_state.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -334,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb
Py_RETURN_NONE;
}
+PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc,
+ ".. function:: framebuffer_active_get(enable)\n"
+ "\n"
+ " Return the active framefuffer in context.\n");
+static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self))
+{
+ GPUFrameBuffer *fb = GPU_framebuffer_active_get();
+ return BPyGPUFrameBuffer_CreatePyObject(fb, true);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -396,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = {
(PyCFunction)pygpu_state_program_point_size_set,
METH_O,
pygpu_state_program_point_size_set_doc},
+ {"active_framebuffer_get",
+ (PyCFunction)pygpu_state_framebuffer_active_get,
+ METH_NOARGS,
+ pygpu_state_framebuffer_active_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c
index 1ae65c1dd11..4df61d35d4c 100644
--- a/source/blender/python/gpu/gpu_py_texture.c
+++ b/source/blender/python/gpu/gpu_py_texture.c
@@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg
return NULL;
}
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, false);
}
PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`");
@@ -412,6 +412,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self)
static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self)
{
if (self->tex) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_texture_py_reference_set(self->tex, NULL);
+#endif
GPU_texture_free(self->tex);
}
Py_TYPE(self)->tp_free((PyObject *)self);
@@ -535,10 +538,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg)
BKE_imageuser_default(&iuser);
GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
- /* Increase the texture reference count. */
- GPU_texture_ref(tex);
-
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, true);
}
static struct PyMethodDef pygpu_texture__m_methods[] = {
@@ -595,13 +595,33 @@ PyObject *bpygpu_texture_init(void)
/** \name Public API
* \{ */
-PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex)
+PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference)
{
BPyGPUTexture *self;
+ if (shared_reference) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ void **ref = GPU_texture_py_reference_get(tex);
+ if (ref) {
+ /* Retrieve BPyGPUTexture reference. */
+ self = (BPyGPUTexture *)POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex));
+ BLI_assert(self->tex == tex);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+#endif
+
+ GPU_texture_ref(tex);
+ }
+
self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type);
self->tex = tex;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ BLI_assert(GPU_texture_py_reference_get(tex) == NULL);
+ GPU_texture_py_reference_set(tex, (void **)&self->tex);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_texture.h b/source/blender/python/gpu/gpu_py_texture.h
index 5130273f971..3eaaa3411bd 100644
--- a/source/blender/python/gpu/gpu_py_texture.h
+++ b/source/blender/python/gpu/gpu_py_texture.h
@@ -33,4 +33,5 @@ typedef struct BPyGPUTexture {
int bpygpu_ParseTexture(PyObject *o, void *p);
PyObject *bpygpu_texture_init(void);
-PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1);
+PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 9ac8d4d9f47..2be2105d327 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
bpy_rna_driver.c
bpy_rna_gizmo.c
bpy_rna_id_collection.c
+ bpy_rna_operator.c
bpy_rna_types_capi.c
bpy_rna_ui.c
bpy_traceback.c
@@ -118,6 +119,7 @@ set(SRC
bpy_rna_driver.h
bpy_rna_gizmo.h
bpy_rna_id_collection.h
+ bpy_rna_operator.h
bpy_rna_types_capi.h
bpy_rna_ui.h
bpy_traceback.h
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index 8ecee9b3f2e..a0b543097e6 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -74,6 +74,7 @@ static PyStructSequence_Field app_cb_info_fields[] = {
{"version_update", "on ending the versioning code"},
{"load_factory_preferences_post", "on loading factory preferences (after)"},
{"load_factory_startup_post", "on loading factory startup (after)"},
+ {"xr_session_start_pre", "on starting an xr session (before)"},
/* sets the permanent tag */
#define APP_CB_OTHER_FIELDS 1
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 5f31e0bb74d..4144063cf5c 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
}
}
+static void bpy_context_end(bContext *C)
+{
+ if (UNLIKELY(C == NULL)) {
+ return;
+ }
+ CTX_wm_operator_poll_msg_clear(C);
+}
+
/**
* Use for `CTX_*_set(..)` functions need to set values which are later read back as expected.
* In this case we don't want the Python context to override the values as it causes problems
@@ -524,6 +532,9 @@ void BPY_python_end(void)
/* finalizing, no need to grab the state, except when we are a module */
gilstate = PyGILState_Ensure();
+ /* Clear Python values in the context so freeing the context after Python exits doesn't crash. */
+ bpy_context_end(BPY_context_get());
+
/* Decrement user counts of all callback functions. */
BPY_rna_props_clear_all();
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 94ad6a8ef78..4a5e2552598 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
}
if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
- const char *msg = CTX_wm_operator_poll_msg_get(C);
+ bool msg_free = false;
+ const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
PyErr_Format(PyExc_RuntimeError,
"Operator bpy.ops.%.200s.poll() %.200s",
opname,
msg ? msg : "failed, context is incorrect");
- CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */
+ CTX_wm_operator_poll_msg_clear(C);
+ if (msg_free) {
+ MEM_freeN((void *)msg);
+ }
error_val = -1;
}
else {
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 1711637458a..fb1cb823964 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -3562,7 +3562,7 @@ PyDoc_STRVAR(pyrna_struct_keys_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property keys.\n"
- " :rtype: list of strings\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewKeys`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
{
@@ -3573,13 +3573,9 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetKeys(group);
+ return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_items_doc,
@@ -3589,7 +3585,7 @@ PyDoc_STRVAR(pyrna_struct_items_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property key, value pairs.\n"
- " :rtype: list of key, value tuples\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewItems`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
{
@@ -3600,13 +3596,9 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetItems(self->ptr.owner_id, group);
+ return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_values_doc,
@@ -3616,7 +3608,7 @@ PyDoc_STRVAR(pyrna_struct_values_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property values.\n"
- " :rtype: list\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewValues`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
{
@@ -3628,13 +3620,9 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetValues(self->ptr.owner_id, group);
+ return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_is_property_set_doc,
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
new file mode 100644
index 00000000000..6e0db3eca49
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -0,0 +1,150 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ *
+ * This file extends `bpy.types.Operator` with C/Python API methods and attributes.
+ */
+
+#include <Python.h>
+
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+
+#include "../generic/python_utildefines.h"
+
+#include "BPY_extern.h"
+#include "bpy_capi_utils.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Operator `poll_message_set` Method
+ * \{ */
+
+static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *py_args = user_data;
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg));
+ }
+
+ PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX);
+ PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first);
+ Py_DECREF(py_args_after_first);
+
+ char *msg = NULL;
+ bool error = false;
+
+ /* NULL for no string. */
+ if (py_msg == NULL) {
+ error = true;
+ }
+ else {
+ if (py_msg == Py_None) {
+ /* pass */
+ }
+ else if (PyUnicode_Check(py_msg)) {
+ msg = BLI_strdup(PyUnicode_AsUTF8(py_msg));
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(function, ...): expected string or None, got %.200s",
+ Py_TYPE(py_msg)->tp_name);
+ error = true;
+ }
+ Py_DECREF(py_msg);
+ }
+
+ if (error) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ PyGILState_Release(gilstate);
+ return msg;
+}
+
+static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data)
+{
+ /* Handles the GIL. */
+ BPY_DECREF(user_data);
+}
+
+PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
+ ".. method:: poll_message_set(message, ...)\n"
+ "\n"
+ " Set the message to show in the tool-tip when poll fails.\n"
+ "\n"
+ " When message is callable, "
+ "additional user defined positional arguments are passed to the message function.\n"
+ "\n"
+ " :param message: The message or a function that returns the message.\n"
+ " :type message: string or a callable that returns a string or None.\n");
+
+static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args)
+{
+ const ssize_t args_len = PyTuple_GET_SIZE(args);
+ if (args_len == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message, ...): requires a message argument");
+ return NULL;
+ }
+
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ if (args_len > 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message): does not support additional arguments");
+ return NULL;
+ }
+ }
+ else if (PyCallable_Check(py_func_or_msg)) {
+ /* pass */
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(message, ...): "
+ "expected at least 1 string or callable argument, got %.200s",
+ Py_TYPE(py_func_or_msg)->tp_name);
+ return NULL;
+ }
+
+ bContext *C = BPY_context_get();
+ struct bContextPollMsgDyn_Params params = {
+ .get_fn = pyop_poll_message_get_fn,
+ .free_fn = pyop_poll_message_free_fn,
+ .user_data = Py_INCREF_RET(args),
+ };
+
+ CTX_wm_operator_poll_msg_set_dynamic(C, &params);
+
+ Py_RETURN_NONE;
+}
+
+PyMethodDef BPY_rna_operator_poll_message_set_method_def = {
+ "poll_message_set",
+ (PyCFunction)BPY_rna_operator_poll_message_set,
+ METH_VARARGS | METH_STATIC,
+ BPY_rna_operator_poll_message_set_doc,
+};
+
+/** \} */
diff --git a/source/blender/python/intern/bpy_rna_operator.h b/source/blender/python/intern/bpy_rna_operator.h
new file mode 100644
index 00000000000..8040d8a492a
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern PyMethodDef BPY_rna_operator_poll_message_set_method_def;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c
index 9b15e84663d..2f6e197d1e2 100644
--- a/source/blender/python/intern/bpy_rna_types_capi.c
+++ b/source/blender/python/intern/bpy_rna_types_capi.c
@@ -41,6 +41,8 @@
#include "bpy_rna_types_capi.h"
#include "bpy_rna_ui.h"
+#include "bpy_rna_operator.h"
+
#include "../generic/py_capi_utils.h"
#include "RNA_access.h"
@@ -87,6 +89,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Operator
+ * \{ */
+
+static struct PyMethodDef pyrna_operator_methods[] = {
+ {NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */
+ {NULL, NULL, 0, NULL},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Window Manager Clipboard Property
*
* Avoid using the RNA API because this value may change between checking its length
@@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void)
/* Space */
pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL);
+ /* wmOperator */
+ ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def);
+ BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2);
+ pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL);
+
/* WindowManager */
pyrna_struct_type_extend_capi(
&RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset);
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 3791a6c2d29..16bf7120606 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -152,7 +152,7 @@ int mathutils_array_parse(
return -1;
}
- memcpy(array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(array, ((const BaseMathObject *)value)->data, size * sizeof(float));
}
else
#endif
@@ -235,7 +235,7 @@ int mathutils_array_parse_alloc(float **array,
}
*array = PyMem_Malloc(size * sizeof(float));
- memcpy(*array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(*array, ((const BaseMathObject *)value)->data, size * sizeof(float));
return size;
}
@@ -471,7 +471,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
return -1;
}
- eulO_to_mat3(rmat, ((EulerObject *)value)->eul, ((EulerObject *)value)->order);
+ eulO_to_mat3(rmat, ((const EulerObject *)value)->eul, ((const EulerObject *)value)->order);
return 0;
}
if (QuaternionObject_Check(value)) {
@@ -480,7 +480,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
}
float tquat[4];
- normalize_qt_qt(tquat, ((QuaternionObject *)value)->quat);
+ normalize_qt_qt(tquat, ((const QuaternionObject *)value)->quat);
quat_to_mat3(rmat, tquat);
return 0;
}
@@ -530,8 +530,8 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
{
/* solid, fast routine across all platforms
* with constant time behavior */
- const int ai = *(int *)(&af);
- const int bi = *(int *)(&bf);
+ const int ai = *(const int *)(&af);
+ const int bi = *(const int *)(&bf);
const int test = SIGNMASK(ai ^ bi);
int diff, v1, v2;
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index f2a8af18073..b6a0183d04e 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -88,7 +88,7 @@ short euler_order_from_string(const char *str, const char *error_prefix)
# define MAKE_ID3(a, b, c) (((a) << 24) | ((b) << 16) | ((c) << 8))
#endif
- switch (*((PY_INT32_T *)str)) {
+ switch (*((const PY_INT32_T *)str)) {
case MAKE_ID3('X', 'Y', 'Z'):
return EULER_ORDER_XYZ;
case MAKE_ID3('X', 'Z', 'Y'):
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 161d2f41592..c312012b24c 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -187,7 +187,7 @@ static int mathutils_matrix_col_get(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
bmo->data[row] = MATRIX_ITEM(self, row, col);
@@ -210,7 +210,7 @@ static int mathutils_matrix_col_set(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
MATRIX_ITEM(self, row, col) = bmo->data[row];
@@ -969,6 +969,104 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args)
return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls);
}
+PyDoc_STRVAR(
+ C_Matrix_LocRotScale_doc,
+ ".. classmethod:: LocRotScale(location, rotation, scale)\n"
+ "\n"
+ " Create a matrix combining translation, rotation and scale,\n"
+ " acting as the inverse of the decompose() method.\n"
+ "\n"
+ " Any of the inputs may be replaced with None if not needed.\n"
+ "\n"
+ " :arg location: The translation component.\n"
+ " :type location: :class:`Vector` or None\n"
+ " :arg rotation: The rotation component.\n"
+ " :type rotation: 3x3 :class:`Matrix`, :class:`Quaternion`, :class:`Euler` or None\n"
+ " :arg scale: The scale component.\n"
+ " :type scale: :class:`Vector` or None\n"
+ " :return: Combined transformation matrix. \n"
+ " :rtype: 4x4 :class:`Matrix`\n");
+static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args)
+{
+ PyObject *loc_obj, *rot_obj, *scale_obj;
+ float mat[4][4], loc[3];
+
+ if (!PyArg_ParseTuple(args, "OOO:Matrix.LocRotScale", &loc_obj, &rot_obj, &scale_obj)) {
+ return NULL;
+ }
+
+ /* Decode location. */
+ if (loc_obj == Py_None) {
+ zero_v3(loc);
+ }
+ else if (mathutils_array_parse(
+ loc, 3, 3, loc_obj, "Matrix.LocRotScale(), invalid location argument") == -1) {
+ return NULL;
+ }
+
+ /* Decode rotation. */
+ if (rot_obj == Py_None) {
+ unit_m4(mat);
+ }
+ else if (QuaternionObject_Check(rot_obj)) {
+ QuaternionObject *quat_obj = (QuaternionObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(quat_obj) == -1) {
+ return NULL;
+ }
+
+ quat_to_mat4(mat, quat_obj->quat);
+ }
+ else if (EulerObject_Check(rot_obj)) {
+ EulerObject *eul_obj = (EulerObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(eul_obj) == -1) {
+ return NULL;
+ }
+
+ eulO_to_mat4(mat, eul_obj->eul, eul_obj->order);
+ }
+ else if (MatrixObject_Check(rot_obj)) {
+ MatrixObject *mat_obj = (MatrixObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(mat_obj) == -1) {
+ return NULL;
+ }
+
+ if (mat_obj->num_col == 3 && mat_obj->num_row == 3) {
+ copy_m4_m3(mat, (const float(*)[3])mat_obj->matrix);
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "inappropriate rotation matrix size - expects 3x3 matrix");
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "rotation argument must be Matrix, Quaternion, Euler or None");
+ return NULL;
+ }
+
+ /* Decode scale. */
+ if (scale_obj != Py_None) {
+ float scale[3];
+
+ if (mathutils_array_parse(
+ scale, 3, 3, scale_obj, "Matrix.LocRotScale(), invalid scale argument") == -1) {
+ return NULL;
+ }
+
+ rescale_m4(mat, scale);
+ }
+
+ copy_v3_v3(mat[3], loc);
+
+ return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls);
+}
+
void matrix_as_3x3(float mat[3][3], MatrixObject *self)
{
copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0));
@@ -1029,7 +1127,7 @@ static float matrix_determinant_internal(const MatrixObject *self)
MATRIX_ITEM(self, 2, 2));
}
- return determinant_m4((float(*)[4])self->matrix);
+ return determinant_m4((const float(*)[4])self->matrix);
}
static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim)
@@ -1037,15 +1135,15 @@ static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort
/* calculate the classical adjoint */
switch (dim) {
case 2: {
- adjoint_m2_m2((float(*)[2])mat_dst, (float(*)[2])mat_src);
+ adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src);
break;
}
case 3: {
- adjoint_m3_m3((float(*)[3])mat_dst, (float(*)[3])mat_src);
+ adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src);
break;
}
case 4: {
- adjoint_m4_m4((float(*)[4])mat_dst, (float(*)[4])mat_src);
+ adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src);
break;
}
default:
@@ -1115,7 +1213,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[2] = (float(*)[2])in_mat;
if (in_mat != self->matrix) {
- copy_m2_m2(mat, (float(*)[2])self->matrix);
+ copy_m2_m2(mat, (const float(*)[2])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1130,7 +1228,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[3] = (float(*)[3])in_mat;
if (in_mat != self->matrix) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1146,7 +1244,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[4] = (float(*)[4])in_mat;
if (in_mat != self->matrix) {
- copy_m4_m4(mat, (float(*)[4])self->matrix);
+ copy_m4_m4(mat, (const float(*)[4])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1194,7 +1292,7 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self)
mat3_to_quat(quat, (float(*)[3])self->matrix);
}
else {
- mat4_to_quat(quat, (float(*)[4])self->matrix);
+ mat4_to_quat(quat, (const float(*)[4])self->matrix);
}
return Quaternion_CreatePyObject(quat, NULL);
@@ -1243,10 +1341,10 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args)
/*must be 3-4 cols, 3-4 rows, square matrix */
if (self->num_row == 3 && self->num_col == 3) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
else if (self->num_row == 4 && self->num_col == 4) {
- copy_m3_m4(mat, (float(*)[4])self->matrix);
+ copy_m3_m4(mat, (const float(*)[4])self->matrix);
}
else {
PyErr_SetString(PyExc_ValueError,
@@ -1321,7 +1419,7 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self)
memcpy(mat[col], MATRIX_COL_PTR(self, col), self->num_row * sizeof(float));
}
- copy_m4_m4((float(*)[4])self->matrix, (float(*)[4])mat);
+ copy_m4_m4((float(*)[4])self->matrix, (const float(*)[4])mat);
self->num_col = 4;
self->num_row = 4;
@@ -1479,7 +1577,7 @@ static bool matrix_invert_args_check(const MatrixObject *self, PyObject *args, b
return true;
case 1:
if (check_type) {
- const MatrixObject *fallback = (MatrixObject *)PyTuple_GET_ITEM(args, 0);
+ const MatrixObject *fallback = (const MatrixObject *)PyTuple_GET_ITEM(args, 0);
if (!MatrixObject_Check(fallback)) {
PyErr_SetString(PyExc_TypeError,
"Matrix.invert: "
@@ -1797,7 +1895,7 @@ static PyObject *Matrix_decompose(MatrixObject *self)
return NULL;
}
- mat4_to_loc_rot_size(loc, rot, size, (float(*)[4])self->matrix);
+ mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix);
mat3_to_quat(quat, rot);
ret = PyTuple_New(3);
@@ -2059,7 +2157,7 @@ static PyObject *Matrix_identity(MatrixObject *self)
static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix)
{
- return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
+ return Matrix_CreatePyObject((const float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
}
PyDoc_STRVAR(Matrix_copy_doc,
@@ -2960,10 +3058,10 @@ static PyObject *Matrix_is_negative_get(MatrixObject *self, void *UNUSED(closure
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_negative_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_negative_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_negative_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_negative_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -2982,10 +3080,10 @@ static PyObject *Matrix_is_orthogonal_get(MatrixObject *self, void *UNUSED(closu
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthonormal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthonormal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3005,10 +3103,10 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthogonal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthogonal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3111,6 +3209,10 @@ static struct PyMethodDef Matrix_methods[] = {
(PyCFunction)C_Matrix_OrthoProjection,
METH_VARARGS | METH_CLASS,
C_Matrix_OrthoProjection_doc},
+ {"LocRotScale",
+ (PyCFunction)C_Matrix_LocRotScale,
+ METH_VARARGS | METH_CLASS,
+ C_Matrix_LocRotScale_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c
index 6abb66899d5..bb6a7720c44 100644
--- a/source/blender/python/mathutils/mathutils_interpolate.c
+++ b/source/blender/python/mathutils/mathutils_interpolate.c
@@ -54,24 +54,15 @@ static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *ar
PyObject *point, *veclist, *ret;
int i;
- if (!PyArg_ParseTuple(args, "OO!:poly_3d_calc", &veclist, &vector_Type, &point)) {
+ if (!PyArg_ParseTuple(args, "OO:poly_3d_calc", &veclist, &point)) {
return NULL;
}
- if (BaseMath_ReadCallback((VectorObject *)point) == -1) {
+ if (mathutils_array_parse(
+ fp, 2, 3 | MU_ARRAY_ZERO, point, "pt must be a 2-3 dimensional vector") == -1) {
return NULL;
}
- fp[0] = ((VectorObject *)point)->vec[0];
- fp[1] = ((VectorObject *)point)->vec[1];
- if (((VectorObject *)point)->size > 2) {
- fp[2] = ((VectorObject *)point)->vec[2];
- }
- else {
- /* if its a 2d vector then set the z to be zero */
- fp[2] = 0.0f;
- }
-
len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, __func__);
if (len == -1) {
return NULL;
diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c
index fe8f9ec0334..d54dbc9ab05 100644
--- a/source/blender/python/mathutils/mathutils_kdtree.c
+++ b/source/blender/python/mathutils/mathutils_kdtree.c
@@ -52,7 +52,7 @@ static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject
BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
PyTuple_SET_ITEMS(py_retval,
- Vector_CreatePyObject((float *)nearest->co, 3, NULL),
+ Vector_CreatePyObject(nearest->co, 3, NULL),
PyLong_FromLong(nearest->index),
PyFloat_FromDouble(nearest->dist));
}