[Bf-blender-cvs] [95164a0] master: BGE: generic python callback list + replace KX_PythonSeq.
Porteries Tristan
noreply at git.blender.org
Mon Oct 26 20:24:27 CET 2015
Commit: 95164a09a73b5f72cfb21abfe608dfd0873b7706
Author: Porteries Tristan
Date: Mon Oct 26 20:27:08 2015 +0100
Branches: master
https://developer.blender.org/rB95164a09a73b5f72cfb21abfe608dfd0873b7706
BGE: generic python callback list + replace KX_PythonSeq.
I made this patch to declared a python list without converting all elements in python object (too slow) or use a CListValue which required CValue items (too expensive in memory). In the case of a big list of points like a collision contacts points list, to use a CListValue we must implement a new class based on CValue for 3D vector to create a python proxy even if mathutils do it perfectly, we must also convert all points (frequently ~100 points) when fill the CListValue even if the list [...]
5 callbacks are used :
- Check if the list is valid = allow acces (like PyObjectPlus.invalid)
- Get the list size
- Get an item in the list by index.
- Get an item name in the list by index (used for operator `list["name"]`)
- Set an item in the list at the index position.
All of these callback take as first argument the client instance.
Why do we use a void * for the client instance ? : In KX_PythonInitTypes.cpp we have to initialize each python inherited class, if we use a template (the only other way) we must add this class each time we use a new type with in KX_PythonInitTypes.cpp
To check if the list can be accessed from python by the user, we check if the python proxy, which is the `m_base` member, is still a valid proxy like in PyObjectPlus. But we can use a callback for more control of user access (e.g a list of collision point invalidate a frame later, in this case no real python owner).
This python list is easily defined with :
```
CPythonCallBackList(
void *client, // The client instance
PyObject *base, // The python instance which owned this list, used to know if the list is valid (like in KX_PythonSeq)
bool (*checkValid)(void *), // A callback to check if this list is till valid (optional)
int (*getSize)(void *), // A callback to get size
PyObject *(*getItem)(void *, int), // A callback to get an item
const char *(*getItemName)(void *, int), // A callback to get an item name (optional) use for acces by string key
bool (*setItem)(void *, int, PyObject *) // A callback to set an item (optional)
)
```
To show its usecase i replaced the odd KX_PythonSeq, it modify KX_Gameobject.sensors/controllers/actuators, SCA_IController.sensors/actuators and BL_ArmatureObject.constraints/channels.
Example : {F245193}, See message in console, press R to erase the object and see invalid proxy error message.
Reviewers: brita_, #game_python, youle, campbellbarton, moguri, agoose77, sergey
Reviewed By: campbellbarton, moguri, agoose77, sergey
Subscribers: sergey
Projects: #game_engine
Differential Revision: https://developer.blender.org/D1363
===================================================================
M source/gameengine/Converter/BL_ArmatureChannel.h
M source/gameengine/Converter/BL_ArmatureObject.cpp
M source/gameengine/Expressions/CMakeLists.txt
A source/gameengine/Expressions/EXP_ListWrapper.h
A source/gameengine/Expressions/intern/ListWrapper.cpp
M source/gameengine/GameLogic/SCA_IController.cpp
M source/gameengine/Ketsji/CMakeLists.txt
M source/gameengine/Ketsji/KX_GameObject.cpp
M source/gameengine/Ketsji/KX_PythonInitTypes.cpp
D source/gameengine/Ketsji/KX_PythonSeq.cpp
D source/gameengine/Ketsji/KX_PythonSeq.h
===================================================================
diff --git a/source/gameengine/Converter/BL_ArmatureChannel.h b/source/gameengine/Converter/BL_ArmatureChannel.h
index d349e6e..a07097f 100644
--- a/source/gameengine/Converter/BL_ArmatureChannel.h
+++ b/source/gameengine/Converter/BL_ArmatureChannel.h
@@ -60,6 +60,11 @@ public:
struct bPoseChannel *posechannel);
virtual ~BL_ArmatureChannel();
+ inline const char *GetName()
+ {
+ return m_posechannel->name;
+ }
+
#ifdef WITH_PYTHON
// Python access
virtual PyObject *py_repr(void);
diff --git a/source/gameengine/Converter/BL_ArmatureObject.cpp b/source/gameengine/Converter/BL_ArmatureObject.cpp
index 3d863bf..a1819a8 100644
--- a/source/gameengine/Converter/BL_ArmatureObject.cpp
+++ b/source/gameengine/Converter/BL_ArmatureObject.cpp
@@ -60,10 +60,11 @@ extern "C" {
#include "DNA_scene_types.h"
#include "DNA_constraint_types.h"
#include "RNA_access.h"
-#include "KX_PythonSeq.h"
#include "KX_PythonInit.h"
#include "KX_KetsjiEngine.h"
+#include "EXP_ListWrapper.h"
+
#include "MT_Matrix4x4.h"
/**
@@ -619,16 +620,58 @@ PyAttributeDef BL_ArmatureObject::Attributes[] = {
{NULL} //Sentinel
};
+static int bl_armature_object_get_constraints_size_cb(void *self_v)
+{
+ return ((BL_ArmatureObject *)self_v)->GetConstraintNumber();
+}
+
+static PyObject *bl_armature_object_get_constraints_item_cb(void *self_v, int index)
+{
+ return ((BL_ArmatureObject *)self_v)->GetConstraint(index)->GetProxy();
+}
+
+static const char *bl_armature_object_get_constraints_item_name_cb(void *self_v, int index)
+{
+ return ((BL_ArmatureObject *)self_v)->GetConstraint(index)->GetName();
+}
+
PyObject *BL_ArmatureObject::pyattr_get_constraints(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
- return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CONSTRAINTS);
+ return (new CListWrapper(self_v,
+ ((BL_ArmatureObject *)self_v)->GetProxy(),
+ NULL,
+ bl_armature_object_get_constraints_size_cb,
+ bl_armature_object_get_constraints_item_cb,
+ bl_armature_object_get_constraints_item_name_cb,
+ NULL))->NewProxy(true);
+}
+
+static int bl_armature_object_get_channels_size_cb(void *self_v)
+{
+ return ((BL_ArmatureObject *)self_v)->GetChannelNumber();
+}
+
+static PyObject *bl_armature_object_get_channels_item_cb(void *self_v, int index)
+{
+ return ((BL_ArmatureObject *)self_v)->GetChannel(index)->GetProxy();
+}
+
+static const char *bl_armature_object_get_channels_item_name_cb(void *self_v, int index)
+{
+ return ((BL_ArmatureObject *)self_v)->GetChannel(index)->GetName();
}
PyObject *BL_ArmatureObject::pyattr_get_channels(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
- BL_ArmatureObject* self = static_cast<BL_ArmatureObject*>(self_v);
+ BL_ArmatureObject *self = static_cast<BL_ArmatureObject *>(self_v);
self->LoadChannels(); // make sure we have the channels
- return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CHANNELS);
+ return (new CListWrapper(self_v,
+ self->GetProxy(),
+ NULL,
+ bl_armature_object_get_channels_size_cb,
+ bl_armature_object_get_channels_item_cb,
+ bl_armature_object_get_channels_item_name_cb,
+ NULL))->NewProxy(true);
}
KX_PYMETHODDEF_DOC_NOARGS(BL_ArmatureObject, update,
diff --git a/source/gameengine/Expressions/CMakeLists.txt b/source/gameengine/Expressions/CMakeLists.txt
index 6ab4d3f..9c563a4 100644
--- a/source/gameengine/Expressions/CMakeLists.txt
+++ b/source/gameengine/Expressions/CMakeLists.txt
@@ -54,6 +54,7 @@ set(SRC
intern/StringValue.cpp
intern/Value.cpp
intern/VectorValue.cpp
+ intern/ListWrapper.cpp
EXP_BoolValue.h
EXP_ConstExpr.h
@@ -75,6 +76,8 @@ set(SRC
EXP_Value.h
EXP_VectorValue.h
EXP_VoidValue.h
+ EXP_ListWrapper.h
+
)
if(WITH_PYTHON)
diff --git a/source/gameengine/Expressions/EXP_ListWrapper.h b/source/gameengine/Expressions/EXP_ListWrapper.h
new file mode 100644
index 0000000..e4c9769
--- /dev/null
+++ b/source/gameengine/Expressions/EXP_ListWrapper.h
@@ -0,0 +1,109 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Porteries Tristan.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file EXP_ListWrapper.h
+ * \ingroup expressions
+ */
+
+#ifdef WITH_PYTHON
+
+#ifndef __EXP_LISTWRAPPER_H__
+#define __EXP_LISTWRAPPER_H__
+
+#include "EXP_Value.h"
+
+class CListWrapper : public CValue
+{
+ Py_Header
+private:
+ /** The client instance passed as first argument of each callback.
+ * We use a void * instead of a template to avoid to declare this class
+ * for each use in KX_PythonInitTypes.
+ */
+ void *m_client;
+
+ // The python object which owned this list.
+ PyObject *m_base;
+
+ /// Returns true if the list is still valid, else each call will raise an error.
+ bool (*m_checkValid)(void *);
+
+ /// Returns the list size.
+ int (*m_getSize)(void *);
+
+ /// Returns the list item for the giving index.
+ PyObject *(*m_getItem)(void *, int);
+
+ /// Returns name item for the giving index, used for python operator list["name"].
+ const char *(*m_getItemName)(void *, int);
+
+ /// Sets the nex item to the index place, return false when failed item conversion.
+ bool (*m_setItem)(void *, int, PyObject *);
+
+public:
+ CListWrapper(void *client,
+ PyObject *base,
+ bool (*checkValid)(void *),
+ int (*getSize)(void *),
+ PyObject *(*getItem)(void *, int),
+ const char *(*getItemName)(void *, int),
+ bool (*setItem)(void *, int, PyObject *));
+ ~CListWrapper();
+
+ /// \section Python Interface
+ bool CheckValid();
+ int GetSize();
+ PyObject *GetItem(int index);
+ const char *GetItemName(int index);
+ bool SetItem(int index, PyObject *item);
+ bool AllowSetItem();
+ bool AllowGetItemByName();
+
+ /// \section CValue Inherited Functions.
+ virtual const STR_String &GetText();
+ virtual void SetName(const char *name);
+ virtual STR_String &GetName();
+ virtual CValue *GetReplica();
+ virtual CValue *Calc(VALUE_OPERATOR op, CValue *val);
+ virtual CValue *CalcFinal(VALUE_DATA_TYPE dtype, VALUE_OPERATOR op, CValue *val);
+ virtual double GetNumber();
+ virtual int GetValueType();
+ virtual PyObject *py_repr();
+
+ // Python list operators.
+ static PySequenceMethods py_as_sequence;
+ // Python dictionnary operators.
+ static PyMappingMethods py_as_mapping;
+
+ static Py_ssize_t py_len(PyObject *self);
+ static PyObject *py_get_item(PyObject *self, Py_ssize_t index);
+ static int py_set_item(PyObject *self, Py_ssize_t index, PyObject *value);
+ static PyObject *py_mapping_subscript(PyObject *self, PyObject *key);
+ static int py_mapping_ass_subscript(PyObject *self, PyObject *key, PyObject *value);
+ static int py_contains(PyObject *self, PyObject *key);
+
+ KX_PYMETHOD_VARARGS(CListWrapper, Get);
+};
+
+#endif // __EXP_LISTWRAPPER_H__
+
+#endif // WITH_PYTHON
diff --git a/source/gameengine/Expressions/intern/ListWrapper.cpp b/source/gameengine/Expressions/intern/ListWrapper.cpp
new file mode 100644
index 0000000..db1518a
--- /dev/null
+++ b/source/gameengine/Expressions/intern/ListWrapper.cpp
@@ -0,0 +1,424 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Porteries Tristan.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ListWrapper.cpp
+ * \ingroup expressions
+ */
+
+#ifdef WITH_PYTHON
+
+#include "EXP_ListWrapper.h"
+
+static STR_String pythonGeneratorList = "ListWrapper";
+
+CListWrapper::CListWrapper(void *client,
+ PyObject *base,
+ bool (*checkValid)(void *),
+ int (*getSize)(void *),
+ PyObject *(*getItem)(void *, int),
+ const char *(*getItemName)(void *, int),
+ bool (*setItem)(void *, int, PyObject *))
+:m_client(client),
+m_base(base),
+m_checkValid(checkValid),
+m_getSize(getSize),
+m_getItem(getItem),
+m_getItemName(getItemName),
+m_setItem(setItem)
+{
+ // Incref to always have a existing pointer.
+ Py_INCREF(m_base);
+}
+
+CListWrapper::~CListWrapper()
+{
+ Py_DECREF(m_base);
+}
+
+bool CListWrapper::CheckValid()
+{
+ if (m_base && !BGE_PROXY_REF(m_base)) {
+ return false;
+ }
+ return m_checkValid ? (*m_checkValid)(m_client) : true;
+}
+
+int CListWrapper::GetSize()
+{
+ return (*m_getSize)(m_client);
+}
+
+PyObject *CListWrapper::GetItem(int index)
+{
+ return (*m_getItem)(m_client, index);
+}
+
+const char *CListWrapper::GetItemName(int index)
+{
+ return (*m_getItemName)(m_client, index);
+}
+
+bool CListWrapper::SetItem(int index, PyObject *item)
+{
+ return (*m_setItem)(m_client, index, item);
+}
+
+bool CListWrapper::AllowSetItem()
+{
+ return m_setItem != NULL;
+}
+
+bool CListWrapper::AllowGetItemByName()
+{
+ return m_getItemName != NULL;
+}
+
+// ===============================
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list