From f62dca6f3c8028dc69fff5be439fcc5d90a87124 Mon Sep 17 00:00:00 2001 From: marechal-p Date: Sat, 14 Jul 2018 14:10:35 -0400 Subject: [PATCH] UPBGE: Add `scene.onRemove` callback list The callback list is executed when the scene is removed. ```py def example(controller): scene = controller.owner.scene @scene.onRemove.append def callback(scene): print('removing scene "%s"' % scene.name) ``` --- .../rst/bge_types/bge.types.KX_Scene.rst | 18 ++++++-- source/gameengine/Ketsji/KX_KetsjiEngine.cpp | 13 ++++-- source/gameengine/Ketsji/KX_KetsjiEngine.h | 3 +- source/gameengine/Ketsji/KX_Scene.cpp | 44 +++++++++++++++++++ source/gameengine/Ketsji/KX_Scene.h | 6 +++ 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/doc/python_api/rst/bge_types/bge.types.KX_Scene.rst b/doc/python_api/rst/bge_types/bge.types.KX_Scene.rst index 0de1a9a7c096..a0da33a4d894 100644 --- a/doc/python_api/rst/bge_types/bge.types.KX_Scene.rst +++ b/doc/python_api/rst/bge_types/bge.types.KX_Scene.rst @@ -82,9 +82,9 @@ base class --- :class:`EXP_PyObjectPlus` The current active camera. :type: :class:`KX_Camera` - + .. note:: - + This can be set directly from python to avoid using the :class:`KX_SceneActuator`. .. attribute:: overrideCullingCamera @@ -137,11 +137,23 @@ base class --- :class:`EXP_PyObjectPlus` .. attribute:: pre_draw_setup - A list of callables to be run before the drawing setup (i.e., before the model view and projection matrices are computed). + A list of callables to be run before the drawing setup (i.e., before the model view and projection matrices are computed). The callbacks can take as argument the rendered camera, the camera could be temporary in case of stereo rendering. :type: list + .. attribute:: onRemove + + A list of callables to run when the scene is destroyed. + + .. code-block:: python + + @scene.onRemove.append + def callback(scene): + print('exiting %s...' % scene.name) + + :type: list + .. attribute:: gravity The scene gravity using the world x, y and z axis. diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index 14c7b772d70e..919432ed2175 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -1054,7 +1054,7 @@ void KX_KetsjiEngine::StopEngine() while (m_scenes->GetCount() > 0) { KX_Scene *scene = m_scenes->GetFront(); - m_converter->RemoveScene(scene); + DestructScene(scene); // WARNING: here the scene is a dangling pointer. m_scenes->Remove(0); } @@ -1296,7 +1296,7 @@ void KX_KetsjiEngine::RemoveScheduledScenes() KX_Scene *scene = FindScene(scenename); if (scene) { - m_converter->RemoveScene(scene); + DestructScene(scene); m_scenes->RemoveValue(scene); } } @@ -1408,7 +1408,8 @@ void KX_KetsjiEngine::ReplaceScheduledScenes() // avoid crash if the new scene doesn't exist, just do nothing Scene *blScene = m_converter->GetBlenderSceneForName(newscenename); if (blScene) { - m_converter->RemoveScene(scene); + DestructScene(scene); + m_scenes->RemoveValue(scene); KX_Scene *tmpscene = CreateScene(blScene); ConvertScene(tmpscene); @@ -1443,6 +1444,12 @@ void KX_KetsjiEngine::ResumeScene(const std::string& scenename) } } +void KX_KetsjiEngine::DestructScene(KX_Scene *scene) +{ + scene->RunOnRemoveCallbacks(); + m_converter->RemoveScene(scene); +} + double KX_KetsjiEngine::GetTicRate() { return m_ticrate; diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h index 24902abbb4cc..9f7e98633c07 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.h +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h @@ -362,6 +362,7 @@ class KX_KetsjiEngine : public mt::SimdClassAllocator EXP_ListValue *CurrentScenes(); KX_Scene *FindScene(const std::string& scenename); void AddScene(KX_Scene *scene); + void DestructScene(KX_Scene *scene); void ConvertAndAddScene(const std::string& scenename, bool overlay); void RemoveScene(const std::string& scenename); @@ -441,7 +442,7 @@ class KX_KetsjiEngine : public mt::SimdClassAllocator double GetAverageFrameRate(); /** - * Gets the time scale multiplier + * Gets the time scale multiplier */ double GetTimeScale() const; diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 9ac23c234d2d..ed6be45bc0b7 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -196,6 +196,7 @@ KX_Scene::KX_Scene(SCA_IInputDevice *inputDevice, #ifdef WITH_PYTHON m_attrDict = nullptr; + m_removeCallbacks = nullptr; for (unsigned short i = 0; i < MAX_DRAW_CALLBACK; ++i) { m_drawCallbacks[i] = nullptr; @@ -283,6 +284,7 @@ KX_Scene::~KX_Scene() } // These may be nullptr but the macro checks. + Py_CLEAR(m_removeCallbacks); for (unsigned short i = 0; i < MAX_DRAW_CALLBACK; ++i) { Py_CLEAR(m_drawCallbacks[i]); } @@ -1731,6 +1733,17 @@ void KX_Scene::RunDrawingCallbacks(DrawingCallbackType callbackType, KX_Camera * } } +void KX_Scene::RunOnRemoveCallbacks() +{ + PyObject *list = m_removeCallbacks; + if (!list || PyList_GET_SIZE(list) == 0) { + return; + } + + PyObject *args[1] = { GetProxy() }; + EXP_RunPythonCallBackList(list, args, 0, 1); +} + PyTypeObject KX_Scene::Type = { PyVarObject_HEAD_INIT(nullptr, 0) "KX_Scene", @@ -2056,6 +2069,36 @@ int KX_Scene::pyattr_set_drawing_callback(EXP_PyObjectPlus *self_v, const EXP_PY return PY_SET_ATTR_SUCCESS; } +PyObject *KX_Scene::pyattr_get_remove_callback(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef) +{ + KX_Scene *self = static_cast(self_v); + + if (!self->m_removeCallbacks) { + self->m_removeCallbacks = PyList_New(0); + } + + Py_INCREF(self->m_removeCallbacks); + + return self->m_removeCallbacks; +} + +int KX_Scene::pyattr_set_remove_callback(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_Scene *self = static_cast(self_v); + + if (!PyList_CheckExact(value)) { + PyErr_SetString(PyExc_ValueError, "Expected a list"); + return PY_SET_ATTR_FAIL; + } + + Py_XDECREF(self->m_removeCallbacks); + + Py_INCREF(value); + self->m_removeCallbacks = value; + + return PY_SET_ATTR_SUCCESS; +} + PyObject *KX_Scene::pyattr_get_gravity(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef) { KX_Scene *self = static_cast(self_v); @@ -2090,6 +2133,7 @@ PyAttributeDef KX_Scene::Attributes[] = { EXP_PYATTRIBUTE_RW_FUNCTION("pre_draw", KX_Scene, pyattr_get_drawing_callback, pyattr_set_drawing_callback), EXP_PYATTRIBUTE_RW_FUNCTION("post_draw", KX_Scene, pyattr_get_drawing_callback, pyattr_set_drawing_callback), EXP_PYATTRIBUTE_RW_FUNCTION("pre_draw_setup", KX_Scene, pyattr_get_drawing_callback, pyattr_set_drawing_callback), + EXP_PYATTRIBUTE_RW_FUNCTION("onRemove", KX_Scene, pyattr_get_remove_callback, pyattr_set_remove_callback), EXP_PYATTRIBUTE_RW_FUNCTION("gravity", KX_Scene, pyattr_get_gravity, pyattr_set_gravity), EXP_PYATTRIBUTE_BOOL_RO("suspended", KX_Scene, m_suspend), EXP_PYATTRIBUTE_BOOL_RO("activityCulling", KX_Scene, m_activityCulling), diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h index 4b6c8c6214bf..7996f4fc1586 100644 --- a/source/gameengine/Ketsji/KX_Scene.h +++ b/source/gameengine/Ketsji/KX_Scene.h @@ -108,6 +108,7 @@ class KX_Scene : public EXP_Value, public SCA_IScene #ifdef WITH_PYTHON PyObject *m_attrDict; + PyObject *m_removeCallbacks; PyObject *m_drawCallbacks[MAX_DRAW_CALLBACK]; #endif @@ -455,6 +456,8 @@ class KX_Scene : public EXP_Value, public SCA_IScene static int pyattr_set_overrideCullingCamera(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value); static PyObject *pyattr_get_drawing_callback(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef); static int pyattr_set_drawing_callback(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value); + static PyObject *pyattr_get_remove_callback(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_remove_callback(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value); static PyObject *pyattr_get_gravity(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef); static int pyattr_set_gravity(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value); @@ -464,6 +467,9 @@ class KX_Scene : public EXP_Value, public SCA_IScene /// Run the registered python drawing functions. void RunDrawingCallbacks(DrawingCallbackType callbackType, KX_Camera *camera); + + // Run the registered python callbacks when the scene is removed. + void RunOnRemoveCallbacks(); #endif };