Skip to content

Commit

Permalink
UPBGE: Add scene.onRemove callback list
Browse files Browse the repository at this point in the history
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)
```
  • Loading branch information
paul-marechal committed Jul 22, 2018
1 parent 1851639 commit f62dca6
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 7 deletions.
18 changes: 15 additions & 3 deletions doc/python_api/rst/bge_types/bge.types.KX_Scene.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
13 changes: 10 additions & 3 deletions source/gameengine/Ketsji/KX_KetsjiEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion source/gameengine/Ketsji/KX_KetsjiEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ class KX_KetsjiEngine : public mt::SimdClassAllocator
EXP_ListValue<KX_Scene> *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);
Expand Down Expand Up @@ -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;

Expand Down
44 changes: 44 additions & 0 deletions source/gameengine/Ketsji/KX_Scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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]);
}
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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<KX_Scene *>(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<KX_Scene *>(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<KX_Scene *>(self_v);
Expand Down Expand Up @@ -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),
Expand Down
6 changes: 6 additions & 0 deletions source/gameengine/Ketsji/KX_Scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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);

Expand All @@ -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
};

Expand Down

2 comments on commit f62dca6

@joelgomes1994
Copy link

@joelgomes1994 joelgomes1994 commented on f62dca6 Jul 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice!
Is there plans for a bge.logic.onExit (to be called just before GE exits)? It would be pretty handy.
Nice work!

@paul-marechal
Copy link
Collaborator Author

@paul-marechal paul-marechal commented on f62dca6 Jul 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now try to detect it (GE exit) based on the last scene that gets removed.

But bge.logic.onExit would be very nice to have indeed ! Expect it someday :)

Please sign in to comment.