Browse Source

Refactored setting variants from strings.
Refactored nodes and components to write their type & ID when saving, instead of the parent node doing that.
Do not save attribute types into the XML format scene, as they are known.
When loading a node hierarchy, store also the root node reference into the SceneResolver.
Fixed bugs in async binary scene loading.
Removed the redundant .dat file extension filter from the editor. Instead .bin should be always used for binary scenes.

Lasse Öörni 14 years ago
parent
commit
1e1496e7d4

+ 1 - 1
Bin/Data/Scripts/Editor/EditorUI.as

@@ -4,7 +4,7 @@ XMLFile@ uiStyle;
 UIElement@ uiMenuBar;
 UIElement@ uiMenuBar;
 FileSelector@ uiFileSelector;
 FileSelector@ uiFileSelector;
 
 
-Array<String> uiSceneFilters = {"*.xml", "*.bin", "*.dat", "*.*"};
+Array<String> uiSceneFilters = {"*.xml", "*.bin", "*.*"};
 Array<String> uiAllFilter = {"*.*"};
 Array<String> uiAllFilter = {"*.*"};
 Array<String> uiScriptFilter = {"*.as", "*.*"};
 Array<String> uiScriptFilter = {"*.as", "*.*"};
 uint uiSceneFilter = 0;
 uint uiSceneFilter = 0;

+ 1 - 0
Docs/ScriptAPI.dox

@@ -676,6 +676,7 @@ Methods:<br>
 - Variant[]@ GetVariantVector() const
 - Variant[]@ GetVariantVector() const
 - const VariantMap& GetVariantMap() const
 - const VariantMap& GetVariantMap() const
 - void FromString(const String&, const String&)
 - void FromString(const String&, const String&)
+- void FromString(VariantType, const String&)
 - String ToString() const
 - String ToString() const
 - VectorBuffer GetBuffer() const
 - VectorBuffer GetBuffer() const
 - Node@ GetNode() const
 - Node@ GetNode() const

+ 90 - 48
Engine/Core/Variant.cpp

@@ -52,7 +52,8 @@ static const String typeNames[] =
     "ResourceRef",
     "ResourceRef",
     "ResourceRefList",
     "ResourceRefList",
     "VariantVector",
     "VariantVector",
-    "VariantMap"
+    "VariantMap",
+    ""
 };
 };
 
 
 Variant& Variant::operator = (const Variant& rhs)
 Variant& Variant::operator = (const Variant& rhs)
@@ -155,67 +156,95 @@ bool Variant::operator == (const Variant& rhs) const
 
 
 void Variant::FromString(const String& type, const String& value)
 void Variant::FromString(const String& type, const String& value)
 {
 {
-    String typeLower = type.ToLower();
-    
-    if (typeLower == "none")
-        SetType(VAR_NONE);
-    else if (typeLower == "int")
+    return FromString(GetTypeFromName(type), value);
+}
+
+void Variant::FromString(VariantType type, const String& value)
+{
+    switch (type)
+    {
+    case VAR_INT:
         *this = ToInt(value);
         *this = ToInt(value);
-    else if (typeLower == "bool")
+        break;
+        
+    case VAR_BOOL:
         *this = ToBool(value);
         *this = ToBool(value);
-    else if (typeLower == "float")
+        break;
+        
+    case VAR_FLOAT:
         *this = ToFloat(value);
         *this = ToFloat(value);
-    else if (typeLower == "vector2")
+        break;
+        
+    case VAR_VECTOR2:
         *this = ToVector2(value);
         *this = ToVector2(value);
-    else if (typeLower == "vector3")
+        break;
+        
+    case VAR_VECTOR3:
         *this = ToVector3(value);
         *this = ToVector3(value);
-    else if (typeLower == "vector4")
+        break;
+        
+    case VAR_VECTOR4:
         *this = ToVector4(value);
         *this = ToVector4(value);
-    else if (typeLower == "quaternion")
+        break;
+        
+    case VAR_QUATERNION:
         *this = ToQuaternion(value);
         *this = ToQuaternion(value);
-    else if (typeLower == "color")
+        break;
+        
+    case VAR_COLOR:
         *this = ToColor(value);
         *this = ToColor(value);
-    else if (typeLower == "string")
+        break;
+        
+    case VAR_STRING:
         *this = value;
         *this = value;
-    else if (typeLower == "buffer")
-    {
-        SetType(VAR_BUFFER);
-        PODVector<unsigned char>& buffer = *(reinterpret_cast<PODVector<unsigned char>*>(&value_));
-        Vector<String> values = value.Split(' ');
-        buffer.Resize(values.Size());
-        for (unsigned i = 0; i < values.Size(); ++i)
-            buffer[i] = ToInt(values[i]);
-    }
-    else if (typeLower == "pointer")
-    {
+        break;
+        
+    case VAR_BUFFER:
+        {
+            SetType(VAR_BUFFER);
+            PODVector<unsigned char>& buffer = *(reinterpret_cast<PODVector<unsigned char>*>(&value_));
+            Vector<String> values = value.Split(' ');
+            buffer.Resize(values.Size());
+            for (unsigned i = 0; i < values.Size(); ++i)
+                buffer[i] = ToInt(values[i]);
+        }
+        break;
+        
+    case VAR_PTR:
         *this = (void*)0;
         *this = (void*)0;
-    }
-    else if (typeLower == "objectref")
-    {
-        Vector<String> values = value.Split(';');
-        if (values.Size() == 2)
+        break;
+        
+    case VAR_RESOURCEREF:
         {
         {
-            SetType(VAR_RESOURCEREF);
-            ResourceRef& ref = *(reinterpret_cast<ResourceRef*>(&value_));
-            ref.type_ = ShortStringHash(values[0]);
-            ref.id_ = StringHash(values[1]);
+            Vector<String> values = value.Split(';');
+            if (values.Size() == 2)
+            {
+                SetType(VAR_RESOURCEREF);
+                ResourceRef& ref = *(reinterpret_cast<ResourceRef*>(&value_));
+                ref.type_ = ShortStringHash(values[0]);
+                ref.id_ = StringHash(values[1]);
+            }
         }
         }
-    }
-    else if (typeLower == "objectreflist")
-    {
-        Vector<String> values = value.Split(';');
-        if (values.Size() >= 1)
+        break;
+        
+    case VAR_RESOURCEREFLIST:
         {
         {
-            SetType(VAR_RESOURCEREFLIST);
-            ResourceRefList& refList = *(reinterpret_cast<ResourceRefList*>(&value_));
-            refList.type_ = ShortStringHash(values[0]);
-            refList.ids_.Resize(values.Size() - 1);
-            for (unsigned i = 1; i < values.Size(); ++i)
-                refList.ids_[i - 1] = StringHash(values[i]);
+            Vector<String> values = value.Split(';');
+            if (values.Size() >= 1)
+            {
+                SetType(VAR_RESOURCEREFLIST);
+                ResourceRefList& refList = *(reinterpret_cast<ResourceRefList*>(&value_));
+                refList.type_ = ShortStringHash(values[0]);
+                refList.ids_.Resize(values.Size() - 1);
+                for (unsigned i = 1; i < values.Size(); ++i)
+                    refList.ids_[i - 1] = StringHash(values[i]);
+            }
         }
         }
-    }
-    else
+        break;
+        
+    default:
         SetType(VAR_NONE);
         SetType(VAR_NONE);
+    }
 }
 }
 
 
 void Variant::SetBuffer(const void* data, unsigned size)
 void Variant::SetBuffer(const void* data, unsigned size)
@@ -290,7 +319,7 @@ String Variant::ToString() const
         // Reference string serialization requires hash-to-name mapping from the context & subsystems. Can not support here
         // Reference string serialization requires hash-to-name mapping from the context & subsystems. Can not support here
         // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
         // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
         return String();
         return String();
-
+        
     default:
     default:
         return String();
         return String();
     }
     }
@@ -493,3 +522,16 @@ const String& Variant::GetTypeName(VariantType type)
 {
 {
     return typeNames[type];
     return typeNames[type];
 }
 }
+
+VariantType Variant::GetTypeFromName(const String& typeName)
+{
+    unsigned index = 0;
+    while (!typeNames[index].Empty())
+    {
+        if (!typeNames[index].Compare(typeName, false))
+            return (VariantType)index;
+        ++index;
+    }
+    
+    return VAR_NONE;
+}

+ 13 - 2
Engine/Core/Variant.h

@@ -293,6 +293,13 @@ public:
         FromString(type, value);
         FromString(type, value);
     }
     }
     
     
+    /// Construct from type and value.
+    Variant(VariantType type, const String& value) :
+        type_(VAR_NONE)
+    {
+        FromString(type, value);
+    }
+    
     /// Copy-construct from another variant.
     /// Copy-construct from another variant.
     Variant(const Variant& value) :
     Variant(const Variant& value) :
         type_(VAR_NONE)
         type_(VAR_NONE)
@@ -669,8 +676,10 @@ public:
     /// Test for inequality with a ShortStringHash.
     /// Test for inequality with a ShortStringHash.
     bool operator != (const ShortStringHash& rhs) const { return !(*this == rhs); }
     bool operator != (const ShortStringHash& rhs) const { return !(*this == rhs); }
     
     
-    /// %Set from type and value strings. Pointers will be set to null, and VariantBuffer or VariantMap types are not supported.
+    /// %Set from typename and value strings. Pointers will be set to null, and VariantBuffer or VariantMap types are not supported.
     void FromString(const String& type, const String& value);
     void FromString(const String& type, const String& value);
+    /// %Set from type and value string. Pointers will be set to null, and VariantBuffer or VariantMap types are not supported.
+    void FromString(VariantType type, const String& value);
     /// %Set buffer type from a memory area.
     /// %Set buffer type from a memory area.
     void SetBuffer(const void* data, unsigned size);
     void SetBuffer(const void* data, unsigned size);
     
     
@@ -827,8 +836,10 @@ public:
     /// Convert value to string. Pointers are returned as null, and VariantBuffer or VariantMap are not supported and return empty.
     /// Convert value to string. Pointers are returned as null, and VariantBuffer or VariantMap are not supported and return empty.
     String ToString() const;
     String ToString() const;
     
     
-    /// Return type name for enum value.
+    /// Return name for variant type.
     static const String& GetTypeName(VariantType type);
     static const String& GetTypeName(VariantType type);
+    /// Return variant type from type name.
+    static VariantType GetTypeFromName(const String& typeName);
     
     
     /// Empty variant.
     /// Empty variant.
     static const Variant EMPTY;
     static const Variant EMPTY;

+ 14 - 1
Engine/Engine/CoreAPI.cpp

@@ -287,6 +287,16 @@ static void ConstructVariantVariantMap(const VariantMap& value, Variant* ptr)
     new(ptr) Variant(value);
     new(ptr) Variant(value);
 }
 }
 
 
+static void ConstructVariantTypeNameValue(const String& type, const String& value, Variant* ptr)
+{
+    new(ptr) Variant(type, value);
+}
+
+static void ConstructVariantTypeValue(VariantType type, const String& value, Variant* ptr)
+{
+    new(ptr) Variant(type, value);
+}
+
 static void DestructVariant(Variant* ptr)
 static void DestructVariant(Variant* ptr)
 {
 {
     ptr->~Variant();
     ptr->~Variant();
@@ -427,6 +437,8 @@ static void RegisterVariant(asIScriptEngine* engine)
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const ResourceRefList&in)", asFUNCTION(ConstructVariantResourceRefList), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const ResourceRefList&in)", asFUNCTION(ConstructVariantResourceRefList), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const Array<Variant>@+)", asFUNCTION(ConstructVariantVariantVector), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const Array<Variant>@+)", asFUNCTION(ConstructVariantVariantVector), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const VariantMap&in)", asFUNCTION(ConstructVariantVariantMap), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const VariantMap&in)", asFUNCTION(ConstructVariantVariantMap), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const String&in, const String&in)", asFUNCTION(ConstructVariantTypeNameValue), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(VariantType, const String&in)", asFUNCTION(ConstructVariantTypeValue), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructVariant), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructVariant), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "void Clear()", asMETHOD(Variant, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "void Clear()", asMETHOD(Variant, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "Variant& opAssign(const Variant&in)", asMETHODPR(Variant, operator =, (const Variant&), Variant&), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "Variant& opAssign(const Variant&in)", asMETHODPR(Variant, operator =, (const Variant&), Variant&), asCALL_THISCALL);
@@ -479,7 +491,8 @@ static void RegisterVariant(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Variant", "const ResourceRefList& GetResourceRefList() const", asMETHOD(Variant, GetResourceRefList), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "const ResourceRefList& GetResourceRefList() const", asMETHOD(Variant, GetResourceRefList), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "Array<Variant>@ GetVariantVector() const", asFUNCTION(VariantGetVariantVector), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "Array<Variant>@ GetVariantVector() const", asFUNCTION(VariantGetVariantVector), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "const VariantMap& GetVariantMap() const", asMETHOD(Variant, GetVariantMap), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "const VariantMap& GetVariantMap() const", asMETHOD(Variant, GetVariantMap), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Variant", "void FromString(const String&in, const String&in)", asMETHOD(Variant, FromString), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Variant", "void FromString(const String&in, const String&in)", asMETHODPR(Variant, FromString, (const String&, const String&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Variant", "void FromString(VariantType, const String&in)", asMETHODPR(Variant, FromString, (VariantType, const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "String ToString() const", asMETHOD(Variant, ToString), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "String ToString() const", asMETHOD(Variant, ToString), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "VariantType get_type() const", asMETHOD(Variant, GetType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "VariantType get_type() const", asMETHOD(Variant, GetType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "const String &get_typeName() const", asMETHODPR(Variant, GetTypeName, () const, const String&), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "const String &get_typeName() const", asMETHODPR(Variant, GetTypeName, () const, const String&), asCALL_THISCALL);

+ 15 - 5
Engine/Resource/XMLElement.cpp

@@ -195,6 +195,11 @@ bool XMLElement::SetVariant(const Variant& value)
     if (!SetAttribute("type", value.GetTypeName()))
     if (!SetAttribute("type", value.GetTypeName()))
         return false;
         return false;
     
     
+    return SetVariantValue(value);
+}
+
+bool XMLElement::SetVariantValue(const Variant& value)
+{
     switch (value.GetType())
     switch (value.GetType())
     {
     {
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
@@ -509,17 +514,22 @@ String XMLElement::GetStringUpper(const String& name) const
 }
 }
 
 
 Variant XMLElement::GetVariant() const
 Variant XMLElement::GetVariant() const
+{
+    VariantType type = Variant::GetTypeFromName(GetAttribute("type"));  
+    return GetVariantValue(type);
+}
+
+Variant XMLElement::GetVariantValue(VariantType type) const
 {
 {
     Variant ret;
     Variant ret;
     
     
-    String type = GetAttribute("type").ToLower();
-    if (type == "resourceref")
+    if (type == VAR_RESOURCEREF)
         ret = GetResourceRef();
         ret = GetResourceRef();
-    else if (type == "resourcereflist")
+    else if (type == VAR_RESOURCEREFLIST)
         ret = GetResourceRefList();
         ret = GetResourceRefList();
-    else if (type == "variantvector")
+    else if (type == VAR_VARIANTVECTOR)
         ret = GetVariantVector();
         ret = GetVariantVector();
-    else if (type == "variantmap")
+    else if (type == VAR_VARIANTMAP)
         ret = GetVariantMap();
         ret = GetVariantMap();
     else
     else
         ret.FromString(type, GetAttribute("value"));
         ret.FromString(type, GetAttribute("value"));

+ 4 - 0
Engine/Resource/XMLElement.h

@@ -78,6 +78,8 @@ public:
     bool SetString(const String& name, const String& value);
     bool SetString(const String& name, const String& value);
     /// %Set a variant attribute.
     /// %Set a variant attribute.
     bool SetVariant(const Variant& value);
     bool SetVariant(const Variant& value);
+    /// %Set a variant attribute excluding the type.
+    bool SetVariantValue(const Variant& value);
     /// %Set a resource reference attribute.
     /// %Set a resource reference attribute.
     bool SetResourceRef(const ResourceRef& value);
     bool SetResourceRef(const ResourceRef& value);
     /// %Set a resource referene list attribute.
     /// %Set a resource referene list attribute.
@@ -149,6 +151,8 @@ public:
     String GetStringUpper(const String& name) const;
     String GetStringUpper(const String& name) const;
     /// Return a variant attribute, or empty if missing.
     /// Return a variant attribute, or empty if missing.
     Variant GetVariant() const;
     Variant GetVariant() const;
+    /// Return a variant attribute with static type.
+    Variant GetVariantValue(VariantType type) const;
     /// Return a resource reference attribute, or empty if missing.
     /// Return a resource reference attribute, or empty if missing.
     ResourceRef GetResourceRef() const;
     ResourceRef GetResourceRef() const;
     /// Return a resource reference list attribute, or empty if missing.
     /// Return a resource reference list attribute, or empty if missing.

+ 24 - 0
Engine/Scene/Component.cpp

@@ -40,6 +40,30 @@ Component::~Component()
 {
 {
 }
 }
 
 
+bool Component::Save(Serializer& dest)
+{
+    // Write type and ID
+    if (!dest.WriteShortStringHash(GetType()))
+        return false;
+    if (!dest.WriteUInt(id_))
+        return false;
+    
+    // Write attributes
+    return Serializable::Save(dest);
+}
+
+bool Component::SaveXML(XMLElement& dest)
+{
+    // Write type and ID
+    if (!dest.SetString("type", GetTypeName()))
+        return false;
+    if (!dest.SetInt("id", id_))
+        return false;
+    
+    // Write attributes
+    return Serializable::SaveXML(dest);
+}
+
 void Component::Remove()
 void Component::Remove()
 {
 {
     if (node_)
     if (node_)

+ 4 - 0
Engine/Scene/Component.h

@@ -42,6 +42,10 @@ public:
     /// Destruct.
     /// Destruct.
     virtual ~Component();
     virtual ~Component();
     
     
+    /// Save as binary data. Return true if successful.
+    virtual bool Save(Serializer& dest);
+    /// Save as XML data. Return true if successful.
+    virtual bool SaveXML(XMLElement& dest);
     /// Handle scene node being assigned at creation.
     /// Handle scene node being assigned at creation.
     virtual void OnNodeSet(Node* node) {};
     virtual void OnNodeSet(Node* node) {};
     /// Handle scene node transform dirtied.
     /// Handle scene node transform dirtied.

+ 29 - 12
Engine/Scene/Node.cpp

@@ -100,6 +100,12 @@ void Node::OnEvent(Object* sender, bool broadcast, StringHash eventType, Variant
 bool Node::Load(Deserializer& source)
 bool Node::Load(Deserializer& source)
 {
 {
     SceneResolver resolver;
     SceneResolver resolver;
+    
+    // Read own ID. Will not be applied, only stored for resolving possible references
+    unsigned nodeID = source.ReadInt();
+    resolver.AddNode(nodeID, this);
+    
+    // Read attributes, components and child nodes
     bool success = Load(source, resolver);
     bool success = Load(source, resolver);
     if (success)
     if (success)
     {
     {
@@ -112,28 +118,32 @@ bool Node::Load(Deserializer& source)
 
 
 bool Node::Save(Serializer& dest)
 bool Node::Save(Serializer& dest)
 {
 {
+    // Write node ID
+    if (!dest.WriteUInt(id_))
+        return false;
+    
+    // Write attributes
     if (!Serializable::Save(dest))
     if (!Serializable::Save(dest))
         return false;
         return false;
     
     
+    // Write components
     dest.WriteVLE(components_.Size());
     dest.WriteVLE(components_.Size());
     for (unsigned i = 0; i < components_.Size(); ++i)
     for (unsigned i = 0; i < components_.Size(); ++i)
     {
     {
         Component* component = components_[i];
         Component* component = components_[i];
         // Create a separate buffer to be able to skip unknown components during deserialization
         // Create a separate buffer to be able to skip unknown components during deserialization
         VectorBuffer compBuffer;
         VectorBuffer compBuffer;
-        compBuffer.WriteShortStringHash(component->GetType());
-        compBuffer.WriteUInt(component->GetID());
         if (!component->Save(compBuffer))
         if (!component->Save(compBuffer))
             return false;
             return false;
         dest.WriteVLE(compBuffer.GetSize());
         dest.WriteVLE(compBuffer.GetSize());
         dest.Write(compBuffer.GetData(), compBuffer.GetSize());
         dest.Write(compBuffer.GetData(), compBuffer.GetSize());
     }
     }
     
     
+    // Write child nodes
     dest.WriteVLE(children_.Size());
     dest.WriteVLE(children_.Size());
     for (unsigned i = 0; i < children_.Size(); ++i)
     for (unsigned i = 0; i < children_.Size(); ++i)
     {
     {
         Node* node = children_[i];
         Node* node = children_[i];
-        dest.WriteUInt(node->GetID());
         if (!node->Save(dest))
         if (!node->Save(dest))
             return false;
             return false;
     }
     }
@@ -145,6 +155,11 @@ bool Node::LoadXML(const XMLElement& source)
 {
 {
     SceneResolver resolver;
     SceneResolver resolver;
     
     
+    // Read own ID. Will not be applied, only stored for resolving possible references
+    unsigned nodeID = source.GetInt("id");
+    resolver.AddNode(nodeID, this);
+    
+    // Read attributes, components and child nodes
     bool success = LoadXML(source, resolver);
     bool success = LoadXML(source, resolver);
     if (success)
     if (success)
     {
     {
@@ -157,26 +172,28 @@ bool Node::LoadXML(const XMLElement& source)
 
 
 bool Node::SaveXML(XMLElement& dest)
 bool Node::SaveXML(XMLElement& dest)
 {
 {
+    // Write node ID
+    if (!dest.SetInt("id", id_))
+        return false;
+    
+    // Write attributes
     if (!Serializable::SaveXML(dest))
     if (!Serializable::SaveXML(dest))
         return false;
         return false;
     
     
+    // Write components
     for (unsigned i = 0; i < components_.Size(); ++i)
     for (unsigned i = 0; i < components_.Size(); ++i)
     {
     {
         Component* component = components_[i];
         Component* component = components_[i];
         XMLElement compElem = dest.CreateChild("component");
         XMLElement compElem = dest.CreateChild("component");
-        
-        compElem.SetString("type", component->GetTypeName());
-        compElem.SetInt("id", component->GetID());
         if (!component->SaveXML(compElem))
         if (!component->SaveXML(compElem))
             return false;
             return false;
     }
     }
     
     
+    // Write child nodes
     for (unsigned i = 0; i < children_.Size(); ++i)
     for (unsigned i = 0; i < children_.Size(); ++i)
     {
     {
         Node* node = children_[i];
         Node* node = children_[i];
         XMLElement childElem = dest.CreateChild("node");
         XMLElement childElem = dest.CreateChild("node");
-        
-        childElem.SetInt("id", node->GetID());
         if (!node->SaveXML(childElem))
         if (!node->SaveXML(childElem))
             return false;
             return false;
     }
     }
@@ -1008,7 +1025,7 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
         VectorBuffer compBuffer(source, source.ReadVLE());
         VectorBuffer compBuffer(source, source.ReadVLE());
         ShortStringHash compType = compBuffer.ReadShortStringHash();
         ShortStringHash compType = compBuffer.ReadShortStringHash();
         unsigned compID = compBuffer.ReadUInt();
         unsigned compID = compBuffer.ReadUInt();
-        Component* newComponent = CreateComponent(compType, compID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Component* newComponent = CreateComponent(compType, compID, compID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         if (newComponent)
         if (newComponent)
         {
         {
             resolver.AddComponent(compID, newComponent);
             resolver.AddComponent(compID, newComponent);
@@ -1024,7 +1041,7 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
     for (unsigned i = 0; i < numChildren; ++i)
     for (unsigned i = 0; i < numChildren; ++i)
     {
     {
         unsigned nodeID = source.ReadUInt();
         unsigned nodeID = source.ReadUInt();
-        Node* newNode = CreateChild(nodeID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Node* newNode = CreateChild(nodeID, nodeID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         resolver.AddNode(nodeID, newNode);
         resolver.AddNode(nodeID, newNode);
         if (!newNode->Load(source, resolver))
         if (!newNode->Load(source, resolver))
             return false;
             return false;
@@ -1047,7 +1064,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     {
     {
         String typeName = compElem.GetString("type");
         String typeName = compElem.GetString("type");
         unsigned compID = compElem.GetInt("id");
         unsigned compID = compElem.GetInt("id");
-        Component* newComponent = CreateComponent(ShortStringHash(typeName), compID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Component* newComponent = CreateComponent(ShortStringHash(typeName), compID, compID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         if (newComponent)
         if (newComponent)
         {
         {
             resolver.AddComponent(compID, newComponent);
             resolver.AddComponent(compID, newComponent);
@@ -1065,7 +1082,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     while (childElem)
     while (childElem)
     {
     {
         unsigned nodeID = childElem.GetInt("id");
         unsigned nodeID = childElem.GetInt("id");
-        Node* newNode = CreateChild(nodeID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Node* newNode = CreateChild(nodeID, nodeID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         resolver.AddNode(nodeID, newNode);
         resolver.AddNode(nodeID, newNode);
         if (!newNode->LoadXML(childElem, resolver))
         if (!newNode->LoadXML(childElem, resolver))
             return false;
             return false;

+ 19 - 5
Engine/Scene/Scene.cpp

@@ -198,8 +198,13 @@ bool Scene::LoadAsync(File* file)
     
     
     LOGINFO("Loading scene from " + file->GetName());
     LOGINFO("Loading scene from " + file->GetName());
     
     
-    // Clear the previous scene and load the root level components first
     Clear();
     Clear();
+    
+    // Store own old ID for resolving possible root node references
+    unsigned nodeID = file->ReadUInt();
+    resolver_.AddNode(nodeID, this);
+    
+    // Load root level components first
     if (!Node::Load(*file, resolver_, false))
     if (!Node::Load(*file, resolver_, false))
         return false;
         return false;
     
     
@@ -230,9 +235,14 @@ bool Scene::LoadAsyncXML(File* file)
     
     
     LOGINFO("Loading scene from " + file->GetName());
     LOGINFO("Loading scene from " + file->GetName());
     
     
-    // Clear the previous scene and load the root level components first
     Clear();
     Clear();
     XMLElement rootElement = xmlFile->GetRoot();
     XMLElement rootElement = xmlFile->GetRoot();
+    
+    // Store own old ID for resolving possible root node references
+    unsigned nodeID = rootElement.GetInt("id");
+    resolver_.AddNode(nodeID, this);
+    
+    // Load the root level components first
     if (!Node::LoadXML(rootElement, resolver_, false))
     if (!Node::LoadXML(rootElement, resolver_, false))
         return false;
         return false;
     
     
@@ -261,6 +271,7 @@ void Scene::StopAsyncLoading()
     asyncProgress_.file_.Reset();
     asyncProgress_.file_.Reset();
     asyncProgress_.xmlFile_.Reset();
     asyncProgress_.xmlFile_.Reset();
     asyncProgress_.xmlElement_ = XMLElement();
     asyncProgress_.xmlElement_ = XMLElement();
+    resolver_.Reset();
 }
 }
 
 
 void Scene::Clear()
 void Scene::Clear()
@@ -270,7 +281,6 @@ void Scene::Clear()
     RemoveAllComponents();
     RemoveAllComponents();
     fileName_ = String();
     fileName_ = String();
     checksum_ = 0;
     checksum_ = 0;
-    resolver_.Reset();
 }
 }
 
 
 void Scene::SetActive(bool enable)
 void Scene::SetActive(bool enable)
@@ -586,12 +596,16 @@ void Scene::UpdateAsyncLoading()
         // Read one child node with its full sub-hierarchy from either from binary or XML
         // Read one child node with its full sub-hierarchy from either from binary or XML
         if (!asyncProgress_.xmlFile_)
         if (!asyncProgress_.xmlFile_)
         {
         {
-            Node* newNode = CreateChild(asyncProgress_.file_->ReadUInt(), REPLICATED);
+            unsigned nodeID = asyncProgress_.file_->ReadUInt();
+            Node* newNode = CreateChild(nodeID, nodeID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+            resolver_.AddNode(nodeID, newNode);
             newNode->Load(*asyncProgress_.file_, resolver_);
             newNode->Load(*asyncProgress_.file_, resolver_);
         }
         }
         else
         else
         {
         {
-            Node* newNode = CreateChild(asyncProgress_.xmlElement_.GetInt("id"), REPLICATED);
+            unsigned nodeID = asyncProgress_.xmlElement_.GetInt("id");
+            Node* newNode = CreateChild(nodeID, nodeID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+            resolver_.AddNode(nodeID, newNode);
             newNode->LoadXML(asyncProgress_.xmlElement_, resolver_);
             newNode->LoadXML(asyncProgress_.xmlElement_, resolver_);
             asyncProgress_.xmlElement_ = asyncProgress_.xmlElement_.GetNext("node");
             asyncProgress_.xmlElement_ = asyncProgress_.xmlElement_.GetNext("node");
         }
         }

+ 0 - 1
Engine/Scene/SceneResolver.cpp

@@ -57,7 +57,6 @@ void SceneResolver::AddComponent(unsigned oldID, Component* component)
 void SceneResolver::Resolve()
 void SceneResolver::Resolve()
 {
 {
     // Nodes do not have component or node ID attributes, so only have to go through components
     // Nodes do not have component or node ID attributes, so only have to go through components
-    /// \todo The root node does not currently save its own ID, so it is unknown to the resolving process
     HashSet<ShortStringHash> noIDAttributes;
     HashSet<ShortStringHash> noIDAttributes;
     for (HashMap<unsigned, WeakPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
     for (HashMap<unsigned, WeakPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
     {
     {

+ 2 - 2
Engine/Scene/Serializable.cpp

@@ -297,7 +297,7 @@ bool Serializable::LoadXML(const XMLElement& source)
                         LOGWARNING("Unknown enum value " + value + " in attribute " + String(attr.name_));
                         LOGWARNING("Unknown enum value " + value + " in attribute " + String(attr.name_));
                 }
                 }
                 else
                 else
-                    OnSetAttribute(attr, attrElem.GetVariant());
+                    OnSetAttribute(attr, attrElem.GetVariantValue(attr.type_));
                 
                 
                 found = true;
                 found = true;
                 break;
                 break;
@@ -345,7 +345,7 @@ bool Serializable::SaveXML(XMLElement& dest)
             attrElem.SetString("value", String(attr.enumNames_[enumValue]));
             attrElem.SetString("value", String(attr.enumNames_[enumValue]));
         }
         }
         else
         else
-            attrElem.SetVariant(value);
+            attrElem.SetVariantValue(value);
     }
     }
     
     
     return true;
     return true;