[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