Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/python_api/rst/bge.logic.rst10
-rw-r--r--doc/python_api/rst/bge.types.rst38
-rw-r--r--source/gameengine/Converter/BL_BlenderDataConversion.cpp10
-rw-r--r--source/gameengine/Converter/CMakeLists.txt2
-rw-r--r--source/gameengine/Converter/KX_BlenderSceneConverter.cpp136
-rw-r--r--source/gameengine/Converter/KX_BlenderSceneConverter.h17
-rw-r--r--source/gameengine/Converter/KX_LibLoadStatus.cpp251
-rw-r--r--source/gameengine/Converter/KX_LibLoadStatus.h85
-rw-r--r--source/gameengine/Ketsji/KX_ISceneConverter.h3
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_PythonInit.cpp20
-rw-r--r--source/gameengine/Ketsji/KX_PythonInitTypes.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_Scene.cpp4
13 files changed, 553 insertions, 27 deletions
diff --git a/doc/python_api/rst/bge.logic.rst b/doc/python_api/rst/bge.logic.rst
index ee39f7659ca..944b1ca06a0 100644
--- a/doc/python_api/rst/bge.logic.rst
+++ b/doc/python_api/rst/bge.logic.rst
@@ -176,7 +176,7 @@ General functions
Restarts the current game by reloading the .blend file (the last saved version, not what is currently running).
-.. function:: LibLoad(blend, type, data, load_actions=False, verbose=False, load_scripts=True)
+.. function:: LibLoad(blend, type, data, load_actions=False, verbose=False, load_scripts=True, async=False)
Converts the all of the datablocks of the given type from the given blend.
@@ -191,7 +191,13 @@ General functions
:arg verbose: Whether or not to print debugging information (e.g., "SceneName: Scene")
:type verbose: bool
:arg load_scripts: Whether or not to load text datablocks as well (can be disabled for some extra security)
- :type load_scripts: bool
+ :type load_scripts: bool
+ :arg async: Whether or not to do the loading asynchronously (in another thread). Only the "Scene" type is currently supported for this feature.
+ :type async: bool
+
+ :rtype: :class:`bge.types.KX_LibLoadStatus`
+
+ .. note:: Asynchronously loaded libraries will not be available immediately after LibLoad() returns. Use the returned KX_LibLoadStatus to figure out when the libraries are ready.
.. function:: LibNew(name, type, data)
diff --git a/doc/python_api/rst/bge.types.rst b/doc/python_api/rst/bge.types.rst
index 85429a00791..470e1c56bac 100644
--- a/doc/python_api/rst/bge.types.rst
+++ b/doc/python_api/rst/bge.types.rst
@@ -1929,6 +1929,44 @@ Types
:type: boolean
+.. class:: KX_LibLoadStatus(PyObjectPlus)
+
+ An object providing information about a LibLoad() operation.
+
+ .. code-block:: python
+
+ # Print a message when an async LibLoad is done
+ import bge
+
+ def finished_cb(status):
+ print("Library (%s) loaded in %.2fms." % (status.libraryName, status.timeTaken))
+
+ bge.logic.LibLoad('myblend.blend', 'Scene', async=True).onFinish = finished_cb
+
+ .. attribute:: onFinish
+
+ A callback that gets called when the lib load is done.
+
+ :type: callable
+
+ .. attribute:: progress
+
+ The current progress of the lib load as a normalized value from 0.0 to 1.0.
+
+ :type: float
+
+ .. attribute:: libraryName
+
+ The name of the library being loaded (the first argument to LibLoad).
+
+ :type: string
+
+ .. attribute:: timeTaken
+
+ The amount of time, in seconds, the lib load took (0 until the operation is complete).
+
+ :type: float
+
.. class:: KX_LightObject(KX_GameObject)
A Light object.
diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
index f9586e32816..874bf614413 100644
--- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp
+++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
@@ -192,6 +192,8 @@ extern "C" {
}
#endif
+#include "BLI_threads.h"
+
static bool default_light_mode = 0;
static std::map<int, SCA_IInputDevice::KX_EnumInputs> create_translate_table()
@@ -1342,7 +1344,13 @@ static float my_boundbox_mesh(Mesh *me, float *loc, float *size)
float radius=0.0f, vert_radius, *co;
int a;
- if (me->bb==0) me->bb= (struct BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
+ if (me->bb==0) {
+ // This can be called in a seperate (not main) thread when doing async libload,
+ // so lets try to be safe...
+ BLI_begin_threaded_malloc();
+ me->bb= (struct BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
+ BLI_end_threaded_malloc();
+ }
bb= me->bb;
INIT_MINMAX(min, max);
diff --git a/source/gameengine/Converter/CMakeLists.txt b/source/gameengine/Converter/CMakeLists.txt
index e9dd97f3821..e01729e156f 100644
--- a/source/gameengine/Converter/CMakeLists.txt
+++ b/source/gameengine/Converter/CMakeLists.txt
@@ -83,6 +83,7 @@ set(SRC
KX_ConvertProperties.cpp
KX_ConvertSensors.cpp
KX_IpoConvert.cpp
+ KX_LibLoadStatus.cpp
KX_SoftBodyDeformer.cpp
BL_ActionActuator.h
@@ -105,6 +106,7 @@ set(SRC
KX_ConvertProperties.h
KX_ConvertSensors.h
KX_IpoConvert.h
+ KX_LibLoadStatus.h
KX_SoftBodyDeformer.h
)
diff --git a/source/gameengine/Converter/KX_BlenderSceneConverter.cpp b/source/gameengine/Converter/KX_BlenderSceneConverter.cpp
index 260ca9ede96..ceaa0a5f5a8 100644
--- a/source/gameengine/Converter/KX_BlenderSceneConverter.cpp
+++ b/source/gameengine/Converter/KX_BlenderSceneConverter.cpp
@@ -60,6 +60,7 @@
#endif
#include "KX_BlenderSceneConverter.h"
+#include "KX_LibLoadStatus.h"
#include "KX_BlenderScalarInterpolator.h"
#include "BL_BlenderDataConversion.h"
#include "BlenderWorldInfo.h"
@@ -113,6 +114,14 @@ extern "C" {
#include "../../blender/blenlib/BLI_linklist.h"
}
+#include <pthread.h>
+
+/* This is used to avoid including pthread.h in KX_BlenderSceneConverter.h */
+typedef struct ThreadInfo {
+ vector<pthread_t> threads;
+ pthread_mutex_t merge_lock;
+} ThreadInfo;
+
KX_BlenderSceneConverter::KX_BlenderSceneConverter(
struct Main* maggie,
class KX_KetsjiEngine* engine
@@ -126,6 +135,8 @@ KX_BlenderSceneConverter::KX_BlenderSceneConverter(
{
tag_main(maggie, 0); /* avoid re-tagging later on */
m_newfilename = "";
+ m_threadinfo = new ThreadInfo();
+ pthread_mutex_init(&m_threadinfo->merge_lock, NULL);
}
@@ -135,6 +146,16 @@ KX_BlenderSceneConverter::~KX_BlenderSceneConverter()
int i;
// delete sumoshapes
+ if (m_threadinfo) {
+ vector<pthread_t>::iterator pit = m_threadinfo->threads.begin();
+ while (pit != m_threadinfo->threads.end()) {
+ pthread_join((*pit), NULL);
+ pit++;
+ }
+
+ pthread_mutex_destroy(&m_threadinfo->merge_lock);
+ delete m_threadinfo;
+ }
int numAdtLists = m_map_blender_to_gameAdtList.size();
for (i=0; i<numAdtLists; i++) {
@@ -299,7 +320,10 @@ void KX_BlenderSceneConverter::ConvertScene(class KX_Scene* destinationscene,
// hook for registration function during conversion.
m_currentScene = destinationscene;
destinationscene->SetSceneConverter(this);
- SG_SetActiveStage(SG_STAGE_CONVERTER);
+
+ // This doesn't really seem to do anything except cause potential issues
+ // when doing threaded conversion, so it's disabled for now.
+ // SG_SetActiveStage(SG_STAGE_CONVERTER);
if (blenderscene)
{
@@ -945,7 +969,67 @@ Main* KX_BlenderSceneConverter::GetMainDynamicPath(const char *path)
return NULL;
}
-bool KX_BlenderSceneConverter::LinkBlendFileMemory(void *data, int length, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options)
+void KX_BlenderSceneConverter::MergeAsyncLoads()
+{
+ vector<KX_Scene*> *merge_scenes;
+
+ vector<KX_LibLoadStatus*>::iterator mit;
+ vector<KX_Scene*>::iterator sit;
+
+ pthread_mutex_lock(&m_threadinfo->merge_lock);
+
+ for (mit=m_mergequeue.begin(); mit!=m_mergequeue.end(); ++mit) {
+ merge_scenes = (vector<KX_Scene*>*)(*mit)->GetData();
+
+ for (sit=merge_scenes->begin(); sit!=merge_scenes->end(); ++sit) {
+ (*mit)->GetMergeScene()->MergeScene(*sit);
+ delete (*sit);
+ }
+
+
+ delete merge_scenes;
+ (*mit)->SetData(NULL);
+
+ (*mit)->Finish();
+ }
+
+ m_mergequeue.clear();
+
+ pthread_mutex_unlock(&m_threadinfo->merge_lock);
+}
+
+void KX_BlenderSceneConverter::AddScenesToMergeQueue(KX_LibLoadStatus *status)
+{
+ pthread_mutex_lock(&m_threadinfo->merge_lock);
+ m_mergequeue.push_back(status);
+ pthread_mutex_unlock(&m_threadinfo->merge_lock);
+}
+
+static void *async_convert(void *ptr)
+{
+ KX_Scene *new_scene = NULL;
+ KX_LibLoadStatus *status = (KX_LibLoadStatus*)ptr;
+ vector<Scene*> *scenes = (vector<Scene*>*)status->GetData();
+ vector<KX_Scene*> *merge_scenes = new vector<KX_Scene*>(); // Deleted in MergeAsyncLoads
+
+ for (unsigned int i=0; i<scenes->size(); ++i) {
+ new_scene = status->GetEngine()->CreateScene((*scenes)[i], true);
+
+ if (new_scene)
+ merge_scenes->push_back(new_scene);
+
+ status->AddProgress((1.f/scenes->size())*0.9f); // We'll call conversion 90% and merging 10% for now
+ }
+
+ delete scenes;
+ status->SetData(merge_scenes);
+
+ status->GetConverter()->AddScenesToMergeQueue(status);
+
+ return NULL;
+}
+
+KX_LibLoadStatus *KX_BlenderSceneConverter::LinkBlendFileMemory(void *data, int length, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options)
{
BlendHandle *bpy_openlib = BLO_blendhandle_from_memory(data, length);
@@ -953,7 +1037,7 @@ bool KX_BlenderSceneConverter::LinkBlendFileMemory(void *data, int length, const
return LinkBlendFile(bpy_openlib, path, group, scene_merge, err_str, options);
}
-bool KX_BlenderSceneConverter::LinkBlendFilePath(const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options)
+KX_LibLoadStatus *KX_BlenderSceneConverter::LinkBlendFilePath(const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options)
{
BlendHandle *bpy_openlib = BLO_blendhandle_from_file((char *)path, NULL);
@@ -985,7 +1069,7 @@ static void load_datablocks(Main *main_newlib, BlendHandle *bpy_openlib, const c
BLO_library_append_end(NULL, main_tmp, &bpy_openlib, idcode, flag);
}
-bool KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options)
+KX_LibLoadStatus *KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options)
{
Main *main_newlib; /* stored as a dynamic 'main' until we free it */
const int idcode = BKE_idcode_from_name(group);
@@ -994,25 +1078,27 @@ bool KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const cha
// TIMEIT_START(bge_link_blend_file);
+ KX_LibLoadStatus *status;
+
/* only scene and mesh supported right now */
if (idcode!=ID_SCE && idcode!=ID_ME &&idcode!=ID_AC) {
snprintf(err_local, sizeof(err_local), "invalid ID type given \"%s\"\n", group);
*err_str= err_local;
BLO_blendhandle_close(bpy_openlib);
- return false;
+ return NULL;
}
if (GetMainDynamicPath(path)) {
snprintf(err_local, sizeof(err_local), "blend file already open \"%s\"\n", path);
*err_str= err_local;
BLO_blendhandle_close(bpy_openlib);
- return false;
+ return NULL;
}
if (bpy_openlib==NULL) {
snprintf(err_local, sizeof(err_local), "could not open blendfile \"%s\"\n", path);
*err_str= err_local;
- return false;
+ return NULL;
}
main_newlib= (Main *)MEM_callocN( sizeof(Main), "BgeMain");
@@ -1039,6 +1125,8 @@ bool KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const cha
strncpy(main_newlib->name, path, sizeof(main_newlib->name));
+ status = new KX_LibLoadStatus(this, m_ketsjiEngine, scene_merge, path);
+
if (idcode==ID_ME) {
/* Convert all new meshes into BGE meshes */
ID* mesh;
@@ -1063,16 +1151,30 @@ bool KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const cha
else if (idcode==ID_SCE) {
/* Merge all new linked in scene into the existing one */
ID *scene;
+ // scenes gets deleted by the thread when it's done using it (look in async_convert())
+ vector<Scene*> *scenes = (options & LIB_LOAD_ASYNC) ? new vector<Scene*>() : NULL;
+
for (scene= (ID *)main_newlib->scene.first; scene; scene= (ID *)scene->next ) {
if (options & LIB_LOAD_VERBOSE)
printf("SceneName: %s\n", scene->name+2);
- /* merge into the base scene */
- KX_Scene* other= m_ketsjiEngine->CreateScene((Scene *)scene, true);
- scene_merge->MergeScene(other);
+ if (options & LIB_LOAD_ASYNC) {
+ scenes->push_back((Scene*)scene);
+ } else {
+ /* merge into the base scene */
+ KX_Scene* other= m_ketsjiEngine->CreateScene((Scene *)scene, true);
+ scene_merge->MergeScene(other);
- // RemoveScene(other); // Don't run this, it frees the entire scene converter data, just delete the scene
- delete other;
+ // RemoveScene(other); // Don't run this, it frees the entire scene converter data, just delete the scene
+ delete other;
+ }
+ }
+
+ if (options & LIB_LOAD_ASYNC) {
+ pthread_t id;
+ status->SetData(scenes);
+ pthread_create(&id, NULL, &async_convert, (void*)status);
+ m_threadinfo->threads.push_back(id);
}
#ifdef WITH_PYTHON
@@ -1093,9 +1195,14 @@ bool KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const cha
}
}
+ if (!(options & LIB_LOAD_ASYNC))
+ status->Finish();
+
+
// TIMEIT_END(bge_link_blend_file);
- return true;
+ m_status_map[main_newlib->name] = status;
+ return status;
}
/* Note m_map_*** are all ok and don't need to be freed
@@ -1382,6 +1489,9 @@ bool KX_BlenderSceneConverter::FreeBlendFile(struct Main *maggie)
removeImportMain(maggie);
#endif
+ delete m_status_map[maggie->name];
+ m_status_map.erase(maggie->name);
+
free_main(maggie);
return true;
diff --git a/source/gameengine/Converter/KX_BlenderSceneConverter.h b/source/gameengine/Converter/KX_BlenderSceneConverter.h
index eddb377dbc7..f7723350eee 100644
--- a/source/gameengine/Converter/KX_BlenderSceneConverter.h
+++ b/source/gameengine/Converter/KX_BlenderSceneConverter.h
@@ -52,6 +52,7 @@ class BL_InterpolatorList;
class BL_Material;
struct Main;
struct Scene;
+struct ThreadInfo;
class KX_BlenderSceneConverter : public KX_ISceneConverter
{
@@ -61,10 +62,16 @@ class KX_BlenderSceneConverter : public KX_ISceneConverter
vector<pair<KX_Scene*,RAS_MeshObject*> > m_meshobjects;
vector<pair<KX_Scene*,BL_Material *> > m_materials;
+ vector<class KX_LibLoadStatus*> m_mergequeue;
+ ThreadInfo *m_threadinfo;
+
// Cached material conversions
map<struct Material*, BL_Material*> m_mat_cache;
map<struct Material*, RAS_IPolyMaterial*> m_polymat_cache;
+ // Saved KX_LibLoadStatus objects
+ map<char *, class KX_LibLoadStatus*> m_status_map;
+
// Should also have a list of collision shapes.
// For the time being this is held in KX_Scene::m_shapes
@@ -159,13 +166,16 @@ public:
struct Main* GetMainDynamicPath(const char *path);
vector<struct Main*> &GetMainDynamic();
- bool LinkBlendFileMemory(void *data, int length, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options);
- bool LinkBlendFilePath(const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options);
- bool LinkBlendFile(struct BlendHandle *bpy_openlib, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options);
+ class KX_LibLoadStatus *LinkBlendFileMemory(void *data, int length, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options);
+ class KX_LibLoadStatus *LinkBlendFilePath(const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options);
+ class KX_LibLoadStatus *LinkBlendFile(struct BlendHandle *bpy_openlib, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options);
bool MergeScene(KX_Scene *to, KX_Scene *from);
RAS_MeshObject *ConvertMeshSpecial(KX_Scene* kx_scene, Main *maggie, const char *name);
bool FreeBlendFile(struct Main *maggie);
bool FreeBlendFile(const char *path);
+
+ virtual void MergeAsyncLoads();
+ void AddScenesToMergeQueue(class KX_LibLoadStatus *status);
void PrintStats() {
printf("BGE STATS!\n");
@@ -195,6 +205,7 @@ public:
LIB_LOAD_LOAD_ACTIONS = 1,
LIB_LOAD_VERBOSE = 2,
LIB_LOAD_LOAD_SCRIPTS = 4,
+ LIB_LOAD_ASYNC = 8,
};
diff --git a/source/gameengine/Converter/KX_LibLoadStatus.cpp b/source/gameengine/Converter/KX_LibLoadStatus.cpp
new file mode 100644
index 00000000000..fb36f232f30
--- /dev/null
+++ b/source/gameengine/Converter/KX_LibLoadStatus.cpp
@@ -0,0 +1,251 @@
+/*
+ * ***** 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): Mitchell Stokes
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file KX_LibLoadStatus.cpp
+ * \ingroup bgeconv
+ */
+
+#include "KX_LibLoadStatus.h"
+#include "PIL_time.h"
+
+KX_LibLoadStatus::KX_LibLoadStatus(class KX_BlenderSceneConverter* kx_converter,
+ class KX_KetsjiEngine* kx_engine,
+ class KX_Scene* merge_scene,
+ const char *path) :
+ m_converter(kx_converter),
+ m_engine(kx_engine),
+ m_mergescene(merge_scene),
+ m_data(NULL),
+ m_libname(path),
+ m_progress(0.f),
+#ifdef WITH_PYTHON
+ m_finish_cb(NULL),
+ m_progress_cb(NULL)
+#endif
+{
+ m_endtime = m_starttime = PIL_check_seconds_timer();
+}
+
+void KX_LibLoadStatus::Finish()
+{
+ m_progress = 1.f;
+ m_endtime = PIL_check_seconds_timer();
+
+ RunFinishCallback();
+ RunProgressCallback();
+}
+
+void KX_LibLoadStatus::RunFinishCallback()
+{
+#ifdef WITH_PYTHON
+ if (m_finish_cb) {
+ PyObject* args = Py_BuildValue("(O)", GetProxy());
+
+ if (!PyObject_Call(m_finish_cb, args, NULL)) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ Py_DECREF(args);
+ }
+#endif
+}
+
+void KX_LibLoadStatus::RunProgressCallback()
+{
+// Progess callbacks are causing threading problems with Python, so they're disabled for now
+#if 0
+#ifdef WITH_PYTHON
+ if (m_progress_cb) {
+ //PyGILState_STATE gstate = PyGILState_Ensure();
+ PyObject* args = Py_BuildValue("(O)", GetProxy());
+
+ if (!PyObject_Call(m_progress_cb, args, NULL)) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ Py_DECREF(args);
+ //PyGILState_Release(gstate);
+ }
+#endif
+#endif
+}
+
+class KX_BlenderSceneConverter *KX_LibLoadStatus::GetConverter()
+{
+ return m_converter;
+}
+
+class KX_KetsjiEngine *KX_LibLoadStatus::GetEngine()
+{
+ return m_engine;
+}
+
+class KX_Scene *KX_LibLoadStatus::GetMergeScene()
+{
+ return m_mergescene;
+}
+
+void KX_LibLoadStatus::SetLibName(const char *name)
+{
+ m_libname = name;
+}
+
+const char *KX_LibLoadStatus::GetLibName()
+{
+ return m_libname;
+}
+
+void KX_LibLoadStatus::SetData(void *data)
+{
+ m_data = data;
+}
+
+void *KX_LibLoadStatus::GetData()
+{
+ return m_data;
+}
+
+void KX_LibLoadStatus::SetProgress(float progress)
+{
+ m_progress = progress;
+ RunProgressCallback();
+}
+
+float KX_LibLoadStatus::GetProgress()
+{
+ return m_progress;
+}
+
+void KX_LibLoadStatus::AddProgress(float progress)
+{
+ m_progress += progress;
+ RunProgressCallback();
+}
+
+#ifdef WITH_PYTHON
+
+PyMethodDef KX_LibLoadStatus::Methods[] =
+{
+ {NULL} //Sentinel
+};
+
+PyAttributeDef KX_LibLoadStatus::Attributes[] = {
+ KX_PYATTRIBUTE_RW_FUNCTION("onFinish", KX_LibLoadStatus, pyattr_get_onfinish, pyattr_set_onfinish),
+ // KX_PYATTRIBUTE_RW_FUNCTION("onProgress", KX_LibLoadStatus, pyattr_get_onprogress, pyattr_set_onprogress),
+ KX_PYATTRIBUTE_FLOAT_RO("progress", KX_LibLoadStatus, m_progress),
+ KX_PYATTRIBUTE_STRING_RO("libraryName", KX_LibLoadStatus, m_libname),
+ KX_PYATTRIBUTE_RO_FUNCTION("timeTaken", KX_LibLoadStatus, pyattr_get_timetaken),
+ { NULL } //Sentinel
+};
+
+PyTypeObject KX_LibLoadStatus::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "KX_LibLoadStatus",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &PyObjectPlus::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+
+PyObject* KX_LibLoadStatus::pyattr_get_onfinish(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_LibLoadStatus* self = static_cast<KX_LibLoadStatus*>(self_v);
+
+ if (self->m_finish_cb) {
+ Py_INCREF(self->m_finish_cb);
+ return self->m_finish_cb;
+ }
+
+ Py_RETURN_NONE;
+}
+
+int KX_LibLoadStatus::pyattr_set_onfinish(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ KX_LibLoadStatus* self = static_cast<KX_LibLoadStatus*>(self_v);
+
+ if (!PyCallable_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "KX_LibLoadStatus.onFinished requires a callable object");
+ return PY_SET_ATTR_FAIL;
+ }
+
+ if (self->m_finish_cb)
+ Py_DECREF(self->m_finish_cb);
+
+ Py_INCREF(value);
+ self->m_finish_cb = value;
+
+ return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject* KX_LibLoadStatus::pyattr_get_onprogress(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_LibLoadStatus* self = static_cast<KX_LibLoadStatus*>(self_v);
+
+ if (self->m_progress_cb) {
+ Py_INCREF(self->m_progress_cb);
+ return self->m_progress_cb;
+ }
+
+ Py_RETURN_NONE;
+}
+
+int KX_LibLoadStatus::pyattr_set_onprogress(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ KX_LibLoadStatus* self = static_cast<KX_LibLoadStatus*>(self_v);
+
+ if (!PyCallable_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "KX_LibLoadStatus.onProgress requires a callable object");
+ return PY_SET_ATTR_FAIL;
+ }
+
+ if (self->m_progress_cb)
+ Py_DECREF(self->m_progress_cb);
+
+ Py_INCREF(value);
+ self->m_progress_cb = value;
+
+ return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject* KX_LibLoadStatus::pyattr_get_timetaken(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_LibLoadStatus* self = static_cast<KX_LibLoadStatus*>(self_v);
+
+ return PyFloat_FromDouble(self->m_endtime - self->m_starttime);
+}
+#endif // WITH_PYTHON
diff --git a/source/gameengine/Converter/KX_LibLoadStatus.h b/source/gameengine/Converter/KX_LibLoadStatus.h
new file mode 100644
index 00000000000..3da7329213b
--- /dev/null
+++ b/source/gameengine/Converter/KX_LibLoadStatus.h
@@ -0,0 +1,85 @@
+/*
+ * ***** 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): Mitchell Stokes
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file KX_LibLoadStatus.h
+ * \ingroup bgeconv
+ */
+
+#ifndef __KX_LIBLOADSTATUS_H__
+#define __KX_LIBLOADSTATUS_H__
+
+#include "PyObjectPlus.h"
+
+class KX_LibLoadStatus : public PyObjectPlus
+{
+ Py_Header
+private:
+ class KX_BlenderSceneConverter* m_converter;
+ class KX_KetsjiEngine* m_engine;
+ class KX_Scene* m_mergescene;
+ void* m_data;
+ STR_String m_libname;
+
+ float m_progress;
+ double m_starttime;
+ double m_endtime;
+
+#ifdef WITH_PYTHON
+ PyObject* m_finish_cb;
+ PyObject* m_progress_cb;
+#endif
+
+public:
+ KX_LibLoadStatus(class KX_BlenderSceneConverter* kx_converter,
+ class KX_KetsjiEngine* kx_engine,
+ class KX_Scene* merge_scene,
+ const char *path);
+
+ void Finish(); // Called when the libload is done
+ void RunFinishCallback();
+ void RunProgressCallback();
+
+ class KX_BlenderSceneConverter *GetConverter();
+ class KX_KetsjiEngine *GetEngine();
+ class KX_Scene *GetMergeScene();
+
+ void SetLibName(const char *name);
+ const char *GetLibName();
+
+ void SetData(void *data);
+ void *GetData();
+
+ void SetProgress(float progress);
+ float GetProgress();
+ void AddProgress(float progress);
+
+#ifdef WITH_PYTHON
+ static PyObject* pyattr_get_onfinish(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static int pyattr_set_onfinish(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+ static PyObject* pyattr_get_onprogress(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static int pyattr_set_onprogress(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+
+ static PyObject* pyattr_get_timetaken(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+#endif
+};
+
+#endif // __KX_LIBLOADSTATUS_H__
diff --git a/source/gameengine/Ketsji/KX_ISceneConverter.h b/source/gameengine/Ketsji/KX_ISceneConverter.h
index 0dbfd7de2c6..7c1d593a81e 100644
--- a/source/gameengine/Ketsji/KX_ISceneConverter.h
+++ b/source/gameengine/Ketsji/KX_ISceneConverter.h
@@ -62,6 +62,9 @@ public:
virtual void RemoveScene(class KX_Scene *scene)=0;
+ // handle any pending merges from asynchronous loads
+ virtual void MergeAsyncLoads()=0;
+
virtual void SetAlwaysUseExpandFraming(bool to_what) = 0;
virtual void SetNewFileName(const STR_String& filename) = 0;
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
index 890b9d4c472..6638b711a1b 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
@@ -592,6 +592,8 @@ bool KX_KetsjiEngine::NextFrame()
m_frameTime += framestep;
+ m_sceneconverter->MergeAsyncLoads();
+
for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); ++sceneit)
// for each scene, call the proceed functions
{
diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp
index df8f6eb44e8..0d39eb844b5 100644
--- a/source/gameengine/Ketsji/KX_PythonInit.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInit.cpp
@@ -136,6 +136,7 @@ extern "C" {
/* for converting new scenes */
#include "KX_BlenderSceneConverter.h"
+#include "KX_LibLoadStatus.h"
#include "KX_MeshProxy.h" /* for creating a new library of mesh objects */
extern "C" {
#include "BKE_idcode.h"
@@ -670,14 +671,15 @@ static PyObject *gLibLoad(PyObject *, PyObject *args, PyObject *kwds)
Py_buffer py_buffer;
py_buffer.buf = NULL;
char *err_str= NULL;
+ KX_LibLoadStatus *status = NULL;
short options=0;
- int load_actions=0, verbose=0, load_scripts=1;
+ int load_actions=0, verbose=0, load_scripts=1, async=0;
- static const char *kwlist[] = {"path", "group", "buffer", "load_actions", "verbose", "load_scripts", NULL};
+ static const char *kwlist[] = {"path", "group", "buffer", "load_actions", "verbose", "load_scripts", "async", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|y*iii:LibLoad", const_cast<char**>(kwlist),
- &path, &group, &py_buffer, &load_actions, &verbose, &load_scripts))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|y*iiIi:LibLoad", const_cast<char**>(kwlist),
+ &path, &group, &py_buffer, &load_actions, &verbose, &load_scripts, &async))
return NULL;
/* setup options */
@@ -687,6 +689,8 @@ static PyObject *gLibLoad(PyObject *, PyObject *args, PyObject *kwds)
options |= KX_BlenderSceneConverter::LIB_LOAD_VERBOSE;
if (load_scripts != 0)
options |= KX_BlenderSceneConverter::LIB_LOAD_LOAD_SCRIPTS;
+ if (async != 0)
+ options |= KX_BlenderSceneConverter::LIB_LOAD_ASYNC;
if (!py_buffer.buf)
{
@@ -695,16 +699,16 @@ static PyObject *gLibLoad(PyObject *, PyObject *args, PyObject *kwds)
BLI_strncpy(abs_path, path, sizeof(abs_path));
BLI_path_abs(abs_path, gp_GamePythonPath);
- if (kx_scene->GetSceneConverter()->LinkBlendFilePath(abs_path, group, kx_scene, &err_str, options)) {
- Py_RETURN_TRUE;
+ if ((status=kx_scene->GetSceneConverter()->LinkBlendFilePath(abs_path, group, kx_scene, &err_str, options))) {
+ return status->GetProxy();
}
}
else
{
- if (kx_scene->GetSceneConverter()->LinkBlendFileMemory(py_buffer.buf, py_buffer.len, path, group, kx_scene, &err_str, options)) {
+ if ((status=kx_scene->GetSceneConverter()->LinkBlendFileMemory(py_buffer.buf, py_buffer.len, path, group, kx_scene, &err_str, options))) {
PyBuffer_Release(&py_buffer);
- Py_RETURN_TRUE;
+ return status->GetProxy();
}
PyBuffer_Release(&py_buffer);
diff --git a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
index 10c210cf16e..c6aa436537a 100644
--- a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
@@ -47,6 +47,7 @@
#include "KX_ConstraintActuator.h"
#include "KX_ConstraintWrapper.h"
#include "KX_GameActuator.h"
+#include "KX_LibLoadStatus.h"
#include "KX_Light.h"
#include "KX_FontObject.h"
#include "KX_MeshProxy.h"
@@ -199,6 +200,7 @@ void initPyTypes(void)
PyType_Ready_Attr(dict, KX_GameActuator, init_getset);
PyType_Ready_Attr(dict, KX_GameObject, init_getset);
PyType_Ready_Attr(dict, KX_IpoActuator, init_getset);
+ PyType_Ready_Attr(dict, KX_LibLoadStatus, init_getset);
PyType_Ready_Attr(dict, KX_LightObject, init_getset);
PyType_Ready_Attr(dict, KX_FontObject, init_getset);
PyType_Ready_Attr(dict, KX_MeshProxy, init_getset);
diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp
index 72be5f57b95..d298d1b4815 100644
--- a/source/gameengine/Ketsji/KX_Scene.cpp
+++ b/source/gameengine/Ketsji/KX_Scene.cpp
@@ -227,9 +227,13 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
}
#ifdef WITH_PYTHON
+ // We might be running in a separate thread (async libload) so
+ // try and grab the GIL to avoid issues
+ PyGILState_STATE gstate = PyGILState_Ensure();
m_attr_dict = PyDict_New(); /* new ref */
m_draw_call_pre = NULL;
m_draw_call_post = NULL;
+ PyGILState_Release(gstate);
#endif
}