[#19258] [patch] Adding drawing capabilities to BGE Python

patch from Mitchell Stokes (moguri)

simple use case
 scene.post_draw = [pyOpenGLFunc]

this only needs to be set once, then the funcion runs each redraw.

note, this patch also changes how python scripts run (not modules): Dont clear the namespace after running a script, since functions still use the namespace, BGE API is now better when dealing with stale data.

made some changes to this patch.
- assigning a list didnt decrement the existing list.
- initialize as NULL rather then a blank list
- dont use string comparisons for the callbacks, pass the python list to use instead.
- dont check the list items are callable. python will display an error if they are not.
- use python list macros that dont do any type checking sine blender does this when assigning the list

---- from tracker, edited since an updated patch changes some things.
Here is a patch to be able to draw to the screen with BGE Python. This will be very handy for GUI stuff. This patch
works by having the user register a callback in the scene. Two options are available KX_Scene.pre_draw
and KX_Scene.post_draw. The difference between these is when Python draws to the screen (before or after the BGE).
Each can take a list of functions. Here is an example that draws a blue semi-transparent
This commit is contained in:
Campbell Barton 2009-11-22 14:42:22 +00:00
parent f4a0c9239f
commit c36f78dd41
4 changed files with 106 additions and 2 deletions

@ -465,7 +465,8 @@ void SCA_PythonController::Trigger(SCA_LogicManager* logicmgr)
{
/* clear after PyErrPrint - seems it can be using
* something in this dictionary and crash? */
PyDict_Clear(excdict);
// This doesn't appear to be needed anymore
//PyDict_Clear(excdict);
Py_DECREF(excdict);
}

@ -1306,7 +1306,13 @@ void KX_KetsjiEngine::RenderFrame(KX_Scene* scene, KX_Camera* cam)
m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true);
SG_SetActiveStage(SG_STAGE_RENDER);
// Run any pre-drawing python callbacks
scene->RunDrawingCallbacks(scene->GetPreDrawCB());
scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools);
// Run any post-drawing python callbacks
scene->RunDrawingCallbacks(scene->GetPostDrawCB());
if (scene->GetPhysicsEnvironment())
scene->GetPhysicsEnvironment()->debugDrawWorld();

@ -211,6 +211,8 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
#ifndef DISABLE_PYTHON
m_attr_dict = PyDict_New(); /* new ref */
m_draw_call_pre = NULL;
m_draw_call_post = NULL;
#endif
}
@ -264,6 +266,9 @@ KX_Scene::~KX_Scene()
#ifndef DISABLE_PYTHON
PyDict_Clear(m_attr_dict);
Py_DECREF(m_attr_dict);
Py_XDECREF(m_draw_call_pre);
Py_XDECREF(m_draw_call_post);
#endif
}
@ -401,6 +406,28 @@ bool KX_Scene::IsClearingZBuffer()
return m_isclearingZbuffer;
}
void KX_Scene::RunDrawingCallbacks(PyObject* cb_list)
{
int len;
if (cb_list && (len=PyList_GET_SIZE(cb_list)))
{
PyObject* func;
PyObject* ret;
// Iterate the list and run the callbacks
for (int pos=0; pos < len; pos++)
{
func= PyList_GET_ITEM(cb_list, pos);
ret= PyObject_CallObject(func, NULL);
if (ret==NULL) {
PyErr_Print();
PyErr_Clear();
}
}
}
}
void KX_Scene::EnableZBufferClearing(bool isclearingZbuffer)
{
m_isclearingZbuffer = isclearingZbuffer;
@ -1997,6 +2024,61 @@ int KX_Scene::pyattr_set_active_camera(void *self_v, const KX_PYATTRIBUTE_DEF *a
return PY_SET_ATTR_SUCCESS;
}
PyObject* KX_Scene::pyattr_get_drawing_callback_pre(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
KX_Scene* self = static_cast<KX_Scene*>(self_v);
if(self->m_draw_call_pre==NULL)
self->m_draw_call_pre= PyList_New(0);
else
Py_INCREF(self->m_draw_call_pre);
return self->m_draw_call_pre;
}
PyObject* KX_Scene::pyattr_get_drawing_callback_post(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
KX_Scene* self = static_cast<KX_Scene*>(self_v);
if(self->m_draw_call_post==NULL)
self->m_draw_call_post= PyList_New(0);
else
Py_INCREF(self->m_draw_call_post);
return self->m_draw_call_post;
}
int KX_Scene::pyattr_set_drawing_callback_pre(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
KX_Scene* self = static_cast<KX_Scene*>(self_v);
if (!PyList_CheckExact(value))
{
PyErr_SetString(PyExc_ValueError, "Expected a list");
return PY_SET_ATTR_FAIL;
}
Py_XDECREF(self->m_draw_call_pre);
Py_INCREF(value);
self->m_draw_call_pre = value;
return PY_SET_ATTR_SUCCESS;
}
int KX_Scene::pyattr_set_drawing_callback_post(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
KX_Scene* self = static_cast<KX_Scene*>(self_v);
if (!PyList_CheckExact(value))
{
PyErr_SetString(PyExc_ValueError, "Expected a list");
return PY_SET_ATTR_FAIL;
}
Py_XDECREF(self->m_draw_call_post);
Py_INCREF(value);
self->m_draw_call_post = value;
return PY_SET_ATTR_SUCCESS;
}
PyAttributeDef KX_Scene::Attributes[] = {
KX_PYATTRIBUTE_RO_FUNCTION("name", KX_Scene, pyattr_get_name),
@ -2005,6 +2087,8 @@ PyAttributeDef KX_Scene::Attributes[] = {
KX_PYATTRIBUTE_RO_FUNCTION("cameras", KX_Scene, pyattr_get_cameras),
KX_PYATTRIBUTE_RO_FUNCTION("lights", KX_Scene, pyattr_get_lights),
KX_PYATTRIBUTE_RW_FUNCTION("active_camera", KX_Scene, pyattr_get_active_camera, pyattr_set_active_camera),
KX_PYATTRIBUTE_RW_FUNCTION("pre_draw", KX_Scene, pyattr_get_drawing_callback_pre, pyattr_set_drawing_callback_pre),
KX_PYATTRIBUTE_RW_FUNCTION("post_draw", KX_Scene, pyattr_get_drawing_callback_post, pyattr_set_drawing_callback_post),
KX_PYATTRIBUTE_BOOL_RO("suspended", KX_Scene, m_suspend),
KX_PYATTRIBUTE_BOOL_RO("activity_culling", KX_Scene, m_activity_culling),
KX_PYATTRIBUTE_FLOAT_RW("activity_culling_radius", 0.5f, FLT_MAX, KX_Scene, m_activity_box_radius),

@ -95,6 +95,8 @@ class KX_Scene : public PyObjectPlus, public SCA_IScene
#ifndef DISABLE_PYTHON
PyObject* m_attr_dict;
PyObject* m_draw_call_pre;
PyObject* m_draw_call_post;
#endif
struct CullingInfo {
@ -102,7 +104,6 @@ class KX_Scene : public PyObjectPlus, public SCA_IScene
CullingInfo(int layer) : m_layer(layer) {}
};
protected:
RAS_BucketManager* m_bucketmanager;
CListValue* m_tempObjectList;
@ -287,6 +288,12 @@ public:
void RenderBuckets(const MT_Transform& cameratransform,
RAS_IRasterizer* rasty,
RAS_IRenderTools* rendertools);
/**
* Run the registered python drawing functions.
*/
void RunDrawingCallbacks(PyObject* cb_list);
/**
* Update all transforms according to the scenegraph.
*/
@ -544,6 +551,10 @@ public:
static PyObject* pyattr_get_cameras(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static PyObject* pyattr_get_active_camera(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static int pyattr_set_active_camera(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
static PyObject* pyattr_get_drawing_callback_pre(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static int pyattr_set_drawing_callback_pre(void *selv_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
static PyObject* pyattr_get_drawing_callback_post(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static int pyattr_set_drawing_callback_post(void *selv_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
virtual PyObject* py_repr(void) { return PyUnicode_FromString(GetName().ReadPtr()); }
@ -551,6 +562,8 @@ public:
static PyMappingMethods Mapping;
static PySequenceMethods Sequence;
PyObject* GetPreDrawCB() { return m_draw_call_pre; };
PyObject* GetPostDrawCB() { return m_draw_call_post; };
#endif
/**