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;
 FileSelector@ uiFileSelector;
 
-Array<String> uiSceneFilters = {"*.xml", "*.bin", "*.dat", "*.*"};
+Array<String> uiSceneFilters = {"*.xml", "*.bin", "*.*"};
 Array<String> uiAllFilter = {"*.*"};
 Array<String> uiScriptFilter = {"*.as", "*.*"};
 uint uiSceneFilter = 0;

+ 1 - 0
Docs/ScriptAPI.dox

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

+ 90 - 48
Engine/Core/Variant.cpp

@@ -52,7 +52,8 @@ static const String typeNames[] =
     "ResourceRef",
     "ResourceRefList",
     "VariantVector",
-    "VariantMap"
+    "VariantMap",
+    ""
 };
 
 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)
 {
-    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);
-    else if (typeLower == "bool")
+        break;
+        
+    case VAR_BOOL:
         *this = ToBool(value);
-    else if (typeLower == "float")
+        break;
+        
+    case VAR_FLOAT:
         *this = ToFloat(value);
-    else if (typeLower == "vector2")
+        break;
+        
+    case VAR_VECTOR2:
         *this = ToVector2(value);
-    else if (typeLower == "vector3")
+        break;
+        
+    case VAR_VECTOR3:
         *this = ToVector3(value);
-    else if (typeLower == "vector4")
+        break;
+        
+    case VAR_VECTOR4:
         *this = ToVector4(value);
-    else if (typeLower == "quaternion")
+        break;
+        
+    case VAR_QUATERNION:
         *this = ToQuaternion(value);
-    else if (typeLower == "color")
+        break;
+        
+    case VAR_COLOR:
         *this = ToColor(value);
-    else if (typeLower == "string")
+        break;
+        
+    case VAR_STRING:
         *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;
-    }
-    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);
+    }
 }
 
 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
         // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
         return String();
-
+        
     default:
         return String();
     }
@@ -493,3 +522,16 @@ const String& Variant::GetTypeName(VariantType 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);
     }
     
+    /// Construct from type and value.
+    Variant(VariantType type, const String& value) :
+        type_(VAR_NONE)
+    {
+        FromString(type, value);
+    }
+    
     /// Copy-construct from another variant.
     Variant(const Variant& value) :
         type_(VAR_NONE)
@@ -669,8 +676,10 @@ public:
     /// Test for inequality with a ShortStringHash.
     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);
+    /// %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.
     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.
     String ToString() const;
     
-    /// Return type name for enum value.
+    /// Return name for variant type.
     static const String& GetTypeName(VariantType type);
+    /// Return variant type from type name.
+    static VariantType GetTypeFromName(const String& typeName);
     
     /// Empty variant.
     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);
 }
 
+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)
 {
     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 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 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->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);
@@ -479,7 +491,8 @@ static void RegisterVariant(asIScriptEngine* engine)
     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", "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", "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);

+ 15 - 5
Engine/Resource/XMLElement.cpp

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

+ 4 - 0
Engine/Resource/XMLElement.h

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

+ 4 - 0
Engine/Scene/Component.h

@@ -42,6 +42,10 @@ public:
     /// Destruct.
     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.
     virtual void OnNodeSet(Node* node) {};
     /// 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)
 {
     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);
     if (success)
     {
@@ -112,28 +118,32 @@ bool Node::Load(Deserializer& source)
 
 bool Node::Save(Serializer& dest)
 {
+    // Write node ID
+    if (!dest.WriteUInt(id_))
+        return false;
+    
+    // Write attributes
     if (!Serializable::Save(dest))
         return false;
     
+    // Write components
     dest.WriteVLE(components_.Size());
     for (unsigned i = 0; i < components_.Size(); ++i)
     {
         Component* component = components_[i];
         // Create a separate buffer to be able to skip unknown components during deserialization
         VectorBuffer compBuffer;
-        compBuffer.WriteShortStringHash(component->GetType());
-        compBuffer.WriteUInt(component->GetID());
         if (!component->Save(compBuffer))
             return false;
         dest.WriteVLE(compBuffer.GetSize());
         dest.Write(compBuffer.GetData(), compBuffer.GetSize());
     }
     
+    // Write child nodes
     dest.WriteVLE(children_.Size());
     for (unsigned i = 0; i < children_.Size(); ++i)
     {
         Node* node = children_[i];
-        dest.WriteUInt(node->GetID());
         if (!node->Save(dest))
             return false;
     }
@@ -145,6 +155,11 @@ bool Node::LoadXML(const XMLElement& source)
 {
     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);
     if (success)
     {
@@ -157,26 +172,28 @@ bool Node::LoadXML(const XMLElement& source)
 
 bool Node::SaveXML(XMLElement& dest)
 {
+    // Write node ID
+    if (!dest.SetInt("id", id_))
+        return false;
+    
+    // Write attributes
     if (!Serializable::SaveXML(dest))
         return false;
     
+    // Write components
     for (unsigned i = 0; i < components_.Size(); ++i)
     {
         Component* component = components_[i];
         XMLElement compElem = dest.CreateChild("component");
-        
-        compElem.SetString("type", component->GetTypeName());
-        compElem.SetInt("id", component->GetID());
         if (!component->SaveXML(compElem))
             return false;
     }
     
+    // Write child nodes
     for (unsigned i = 0; i < children_.Size(); ++i)
     {
         Node* node = children_[i];
         XMLElement childElem = dest.CreateChild("node");
-        
-        childElem.SetInt("id", node->GetID());
         if (!node->SaveXML(childElem))
             return false;
     }
@@ -1008,7 +1025,7 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
         VectorBuffer compBuffer(source, source.ReadVLE());
         ShortStringHash compType = compBuffer.ReadShortStringHash();
         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)
         {
             resolver.AddComponent(compID, newComponent);
@@ -1024,7 +1041,7 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
     for (unsigned i = 0; i < numChildren; ++i)
     {
         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);
         if (!newNode->Load(source, resolver))
             return false;
@@ -1047,7 +1064,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     {
         String typeName = compElem.GetString("type");
         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)
         {
             resolver.AddComponent(compID, newComponent);
@@ -1065,7 +1082,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     while (childElem)
     {
         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);
         if (!newNode->LoadXML(childElem, resolver))
             return false;

+ 19 - 5
Engine/Scene/Scene.cpp

@@ -198,8 +198,13 @@ bool Scene::LoadAsync(File* file)
     
     LOGINFO("Loading scene from " + file->GetName());
     
-    // Clear the previous scene and load the root level components first
     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))
         return false;
     
@@ -230,9 +235,14 @@ bool Scene::LoadAsyncXML(File* file)
     
     LOGINFO("Loading scene from " + file->GetName());
     
-    // Clear the previous scene and load the root level components first
     Clear();
     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))
         return false;
     
@@ -261,6 +271,7 @@ void Scene::StopAsyncLoading()
     asyncProgress_.file_.Reset();
     asyncProgress_.xmlFile_.Reset();
     asyncProgress_.xmlElement_ = XMLElement();
+    resolver_.Reset();
 }
 
 void Scene::Clear()
@@ -270,7 +281,6 @@ void Scene::Clear()
     RemoveAllComponents();
     fileName_ = String();
     checksum_ = 0;
-    resolver_.Reset();
 }
 
 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
         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_);
         }
         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_);
             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()
 {
     // 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;
     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_));
                 }
                 else
-                    OnSetAttribute(attr, attrElem.GetVariant());
+                    OnSetAttribute(attr, attrElem.GetVariantValue(attr.type_));
                 
                 found = true;
                 break;
@@ -345,7 +345,7 @@ bool Serializable::SaveXML(XMLElement& dest)
             attrElem.SetString("value", String(attr.enumNames_[enumValue]));
         }
         else
-            attrElem.SetVariant(value);
+            attrElem.SetVariantValue(value);
     }
     
     return true;