Browse Source

Async scene loading support.

Lasse Öörni 14 years ago
parent
commit
a67db4ba05
6 changed files with 254 additions and 67 deletions
  1. 2 0
      Engine/Engine/SceneAPI.cpp
  2. 77 62
      Engine/Scene/Node.cpp
  3. 4 0
      Engine/Scene/Node.h
  4. 139 3
      Engine/Scene/Scene.cpp
  5. 30 0
      Engine/Scene/Scene.h
  6. 2 2
      Engine/Scene/SceneEvents.h

+ 2 - 0
Engine/Engine/SceneAPI.cpp

@@ -84,6 +84,8 @@ static void RegisterScene(asIScriptEngine* engine)
     RegisterNamedObjectConstructor<Scene>(engine, "Scene");
     engine->RegisterObjectMethod("Scene", "bool LoadXML(File@+)", asFUNCTION(SceneLoadXML), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Scene", "bool SaveXML(File@+)", asFUNCTION(SceneSaveXML), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Scene", "bool LoadAsync(File@+)", asMETHOD(Scene, LoadAsync), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Scene", "bool LoadAsyncXML(File@+)", asMETHOD(Scene, LoadAsyncXML), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "Component@+ GetComponentByID(uint)", asMETHOD(Scene, GetComponentByID), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "Node@+ GetNodeByID(uint)", asMETHOD(Scene, GetNodeByID), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void Update(float)", asMETHOD(Scene, Update), asCALL_THISCALL);

+ 77 - 62
Engine/Scene/Node.cpp

@@ -116,36 +116,7 @@ void Node::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
 
 bool Node::Load(Deserializer& source)
 {
-    // Remove all children and components first in case this is not a fresh load
-    RemoveAllChildren();
-    RemoveAllComponents();
-    
-    // ID has been read at the parent level
-    if (!Serializable::Load(source))
-        return false;
-    
-    unsigned numComponents = source.ReadVLE();
-    for (unsigned i = 0; i < numComponents; ++i)
-    {
-        VectorBuffer compBuffer(source, source.ReadVLE());
-        ShortStringHash newType = compBuffer.ReadShortStringHash();
-        Component* newComponent = CreateComponent(newType, compBuffer.ReadUInt(), false);
-        if (newComponent)
-        {
-            if (!newComponent->Load(compBuffer))
-                return false;
-        }
-    }
-    
-    unsigned numChildren = source.ReadVLE();
-    for (unsigned i = 0; i < numChildren; ++i)
-    {
-        Node* newNode = CreateChild(source.ReadUInt(), false);
-        if (!newNode->Load(source))
-            return false;
-    }
-    
-    return true;
+    return Load(source, true);
 }
 
 bool Node::Save(Serializer& dest)
@@ -181,38 +152,7 @@ bool Node::Save(Serializer& dest)
 
 bool Node::LoadXML(const XMLElement& source)
 {
-    // Remove all children and components first in case this is not a fresh load
-    RemoveAllChildren();
-    RemoveAllComponents();
-    
-    if (!Serializable::LoadXML(source))
-        return false;
-    
-    XMLElement compElem = source.GetChildElement("component");
-    while (compElem)
-    {
-        std::string typeName = compElem.GetString("type");
-        Component* newComponent = CreateComponent(ShortStringHash(compElem.GetString("type")), compElem.GetInt("id"), false);
-        if (newComponent)
-        {
-            if (!newComponent->LoadXML(compElem))
-                return false;
-        }
-        
-        compElem = compElem.GetNextElement("component");
-    }
-    
-    XMLElement childElem = source.GetChildElement("node");
-    while (childElem)
-    {
-        Node* newNode = CreateChild(compElem.GetInt("id"), false);
-        if (!newNode->LoadXML(childElem))
-            return false;
-        
-        childElem = childElem.GetNextElement("node");
-    }
-    
-    return true;
+    return LoadXML(source, true);
 }
 
 bool Node::SaveXML(XMLElement& dest)
@@ -659,6 +599,81 @@ Component* Node::GetComponent(ShortStringHash type, unsigned index) const
     return 0;
 }
 
+bool Node::Load(Deserializer& source, bool readChildren)
+{
+    // Remove all children and components first in case this is not a fresh load
+    RemoveAllChildren();
+    RemoveAllComponents();
+    
+    // ID has been read at the parent level
+    if (!Serializable::Load(source))
+        return false;
+    
+    unsigned numComponents = source.ReadVLE();
+    for (unsigned i = 0; i < numComponents; ++i)
+    {
+        VectorBuffer compBuffer(source, source.ReadVLE());
+        ShortStringHash newType = compBuffer.ReadShortStringHash();
+        Component* newComponent = CreateComponent(newType, compBuffer.ReadUInt(), false);
+        if (newComponent)
+        {
+            if (!newComponent->Load(compBuffer))
+                return false;
+        }
+    }
+    
+    if (!readChildren)
+        return true;
+    
+    unsigned numChildren = source.ReadVLE();
+    for (unsigned i = 0; i < numChildren; ++i)
+    {
+        Node* newNode = CreateChild(source.ReadUInt(), false);
+        if (!newNode->Load(source))
+            return false;
+    }
+    
+    return true;
+}
+
+bool Node::LoadXML(const XMLElement& source, bool readChildren)
+{
+    // Remove all children and components first in case this is not a fresh load
+    RemoveAllChildren();
+    RemoveAllComponents();
+    
+    if (!Serializable::LoadXML(source))
+        return false;
+    
+    XMLElement compElem = source.GetChildElement("component");
+    while (compElem)
+    {
+        std::string typeName = compElem.GetString("type");
+        Component* newComponent = CreateComponent(ShortStringHash(compElem.GetString("type")), compElem.GetInt("id"), false);
+        if (newComponent)
+        {
+            if (!newComponent->LoadXML(compElem))
+                return false;
+        }
+        
+        compElem = compElem.GetNextElement("component");
+    }
+    
+    if (!readChildren)
+        return true;
+    
+    XMLElement childElem = source.GetChildElement("node");
+    while (childElem)
+    {
+        Node* newNode = CreateChild(compElem.GetInt("id"), false);
+        if (!newNode->LoadXML(childElem))
+            return false;
+        
+        childElem = childElem.GetNextElement("node");
+    }
+    
+    return true;
+}
 
 Component* Node::CreateComponent(ShortStringHash type, unsigned id, bool local)
 {

+ 4 - 0
Engine/Scene/Node.h

@@ -237,6 +237,10 @@ public:
     VariantMap vars_;
     
 protected:
+    /// Load components and optionally load child nodes. Used internally
+    bool Load(Deserializer& source, bool loadChildren);
+    /// Load components from XML data and optionally load child nodes. Used internally
+    bool LoadXML(const XMLElement& source, bool loadChildren);
     /// Create a component with specific ID. Used internally
     Component* CreateComponent(ShortStringHash type, unsigned id, bool local);
     /// Create a child node with specific ID. Used internally

+ 139 - 3
Engine/Scene/Scene.cpp

@@ -34,6 +34,9 @@
 
 OBJECTTYPESTATIC(Scene);
 
+static const int ASYNC_LOAD_MIN_FPS = 60;
+static const int ASYNC_LOAD_MAX_MSEC = (int)(1000.0f / ASYNC_LOAD_MIN_FPS);
+
 Scene::Scene(Context* context) :
     Node(context),
     networkMode_(NM_NONETWORK),
@@ -41,7 +44,8 @@ Scene::Scene(Context* context) :
     nonLocalComponentID_(FIRST_NONLOCAL_ID),
     localNodeID_(FIRST_LOCAL_ID),
     localComponentID_(FIRST_LOCAL_ID),
-    active_(true)
+    active_(true),
+    asyncLoading_(false)
 {
     // Assign an ID to self so that nodes can refer to this node as a parent
     SetID(GetFreeNodeID(false));
@@ -78,7 +82,6 @@ bool Scene::Load(Deserializer& source)
     }
     
     // Load the whole scene, then perform post-load if successfully loaded
-    /// \todo Async loading support
     if (Node::Load(source))
     {
         PostLoad();
@@ -103,7 +106,6 @@ bool Scene::Save(Serializer& dest)
 bool Scene::LoadXML(const XMLElement& source)
 {
     // Load the whole scene, then perform post-load if successfully loaded
-    /// \todo Async loading support
     if (Node::LoadXML(source))
     {
         PostLoad();
@@ -132,8 +134,81 @@ bool Scene::SaveXML(Serializer& dest)
     return xml->Save(dest);
 }
 
+bool Scene::LoadAsync(File* file)
+{
+    if (!file)
+    {
+        LOGERROR("Null file for async loading");
+        return false;
+    }
+    
+    // Check ID
+    if (file->ReadID() != "USCN")
+    {
+        LOGERROR(file->GetName() + " is not a valid scene file");
+        return false;
+    }
+    
+    // Load the root level components first
+    if (!Node::Load(*file, false))
+        return false;
+    
+    // Then prepare for loading all root level child nodes in the async update
+    asyncLoading_ = true;
+    asyncProgress_.file_ = file;
+    asyncProgress_.xmlFile_.Reset();
+    asyncProgress_.xmlElement_ = XMLElement();
+    asyncProgress_.loadedNodes_ = 0;
+    asyncProgress_.totalNodes_ = file->ReadVLE();
+    
+    return true;
+}
+
+bool Scene::LoadAsyncXML(File* file)
+{
+    if (!file)
+    {
+        LOGERROR("Null file for async loading");
+        return false;
+    }
+    
+    SharedPtr<XMLFile> xmlFile(new XMLFile(context_));
+    if (!xmlFile->Load(*file))
+        return false;
+    
+    // Load the root level components first
+    XMLElement rootElement = xmlFile->GetRootElement();
+    if (!Node::LoadXML(rootElement))
+        return false;
+    
+    XMLElement childNodeElement = rootElement.GetChildElement("node");
+    
+    // Then prepare for loading all root level child nodes in the async update
+    asyncLoading_ = true;
+    asyncProgress_.file_.Reset();
+    asyncProgress_.xmlFile_ = xmlFile;
+    asyncProgress_.xmlElement_ = childNodeElement;
+    asyncProgress_.loadedNodes_ = 0;
+    asyncProgress_.totalNodes_ = 0;
+    
+    // Count the amount of child nodes
+    while (childNodeElement)
+    {
+        ++asyncProgress_.totalNodes_;
+        childNodeElement = childNodeElement.GetNextElement("node");
+    }
+    
+    return true;
+}
+
 void Scene::Update(float timeStep)
 {
+    if (asyncLoading_)
+    {
+        UpdateAsyncLoad();
+        return;
+    }
+    
     PROFILE(UpdateScene);
     
     using namespace SceneUpdate;
@@ -285,6 +360,67 @@ void Scene::HandleUpdate(StringHash eventType, VariantMap& eventData)
         Update(eventData[P_TIMESTEP].GetFloat());
 }
 
+void Scene::UpdateAsyncLoad()
+{
+    PROFILE(UpdateAsyncLoad);
+    
+    Timer asyncLoadTimer;
+    
+    for (;;)
+    {
+        // Check if everything loaded
+        if (asyncProgress_.loadedNodes_ >= asyncProgress_.totalNodes_)
+        {
+            FinishAsyncLoad();
+            return;
+        }
+        
+        // Read one child node either from binary or XML
+        if (asyncProgress_.file_)
+        {
+            Node* newNode = CreateChild(asyncProgress_.file_->ReadUInt(), false);
+            newNode->Load(*asyncProgress_.file_);
+        }
+        else
+        {
+            Node* newNode = CreateChild(asyncProgress_.xmlElement_.GetInt("id"), false);
+            newNode->LoadXML(asyncProgress_.xmlElement_);
+            asyncProgress_.xmlElement_ = asyncProgress_.xmlElement_.GetNextElement("node");
+        }
+        
+        ++asyncProgress_.loadedNodes_;
+        
+        // Break if time limit elapsed, so that we keep sufficient FPS
+        if (asyncLoadTimer.GetMSec(true) >= ASYNC_LOAD_MAX_MSEC)
+            break;
+    }
+    
+    using namespace AsyncLoadProgress;
+    
+    VariantMap eventData;
+    eventData[P_SCENE] = (void*)this;
+    eventData[P_PROGRESS] = (float)asyncProgress_.loadedNodes_ / (float)asyncProgress_.totalNodes_;
+    eventData[P_LOADEDNODES]  = asyncProgress_.loadedNodes_;
+    eventData[P_TOTALNODES]  = asyncProgress_.totalNodes_;
+    SendEvent(E_ASYNCLOADPROGRESS, eventData);
+}
+
+void Scene::FinishAsyncLoad()
+{
+    PostLoad();
+    
+    asyncLoading_ = false;
+    asyncProgress_.file_.Reset();
+    asyncProgress_.xmlFile_.Reset();
+    asyncProgress_.xmlElement_ = XMLElement();
+    
+    using namespace AsyncLoadFinished;
+    
+    VariantMap eventData;
+    eventData[P_SCENE] = (void*)this;
+    SendEvent(E_ASYNCLOADFINISHED, eventData);
+}
+
 void RegisterSceneLibrary(Context* context)
 {
     Node::RegisterObject(context);

+ 30 - 0
Engine/Scene/Scene.h

@@ -24,6 +24,9 @@
 #pragma once
 
 #include "Node.h"
+#include "XMLElement.h"
+
+class File;
 
 /// First replicated node/component ID
 static const unsigned FIRST_NONLOCAL_ID = 0x1;
@@ -42,6 +45,21 @@ enum NetworkMode
     NM_CLIENT
 };
 
+/// Scene's asynchronous loading progress
+struct AsyncProgress
+{
+    /// File for binary mode
+    SharedPtr<File> file_;
+    /// XML file for XML mode
+    SharedPtr<XMLFile> xmlFile_;
+    /// Current XML element for XML mode
+    XMLElement xmlElement_;
+    /// Loaded root-level nodes
+    unsigned loadedNodes_;
+    /// Total root-level nodes
+    unsigned totalNodes_;
+};
+
 /// Root scene node, represents the whole scene
 class Scene : public Node
 {
@@ -68,6 +86,10 @@ public:
     bool LoadXML(Deserializer& source);
     /// Save to an XML file. Return true if successful
     bool SaveXML(Serializer& dest);
+    /// Load from a binary file asynchronously. Return true if started successfully
+    bool LoadAsync(File* file);
+    /// Load from an XML file asynchronously. Return true if started successfully
+    bool LoadAsyncXML(File* file);
     /// Update scene
     void Update(float timeStep);
     /// Set networking mode
@@ -100,6 +122,10 @@ public:
 private:
     /// Handle the logic update event to update the scene, if active
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
+    /// Update asynchronous loading
+    void UpdateAsyncLoad();
+    /// Finish asynchronous loading
+    void FinishAsyncLoad();
     
     /// Map of scene nodes by ID
     std::map<unsigned, Node*> allNodes_;
@@ -107,6 +133,8 @@ private:
     std::map<unsigned, Component*> allComponents_;
     /// Networking mode
     NetworkMode networkMode_;
+    /// Async loading progress
+    AsyncProgress asyncProgress_;
     /// Next free non-local node ID
     unsigned nonLocalNodeID_;
     /// Next free local node ID
@@ -117,6 +145,8 @@ private:
     unsigned localComponentID_;
     /// Active flag
     bool active_;
+    /// Async loading flag
+    bool asyncLoading_;
 };
 
 /// Register Scene library objects

+ 2 - 2
Engine/Scene/SceneEvents.h

@@ -51,8 +51,8 @@ EVENT(E_ASYNCLOADPROGRESS, AsyncLoadProgress)
 {
     PARAM(P_SCENE, Scene);                // Scene pointer
     PARAM(P_PROGRESS, Progress);          // float
-    PARAM(P_LOADEDENTITIES, LoadedEntities); // int
-    PARAM(P_TOTALENTITIES, TotalEntities); // int
+    PARAM(P_LOADEDNODES, LoadedNodes);    // int
+    PARAM(P_TOTALNODES, TotalNodes);      // int
 };
 
 /// Asynchronous scene loading finished