Browse Source

Add support of arbitrary data in Sound, Animation and Texture resources.

Eugene Kozlov 8 years ago
parent
commit
baf0daf7aa

+ 28 - 1
Source/Urho3D/AngelScript/APITemplates.h

@@ -834,6 +834,33 @@ template <class T> void RegisterResource(asIScriptEngine* engine, const char* cl
     engine->RegisterObjectMethod(className, "uint get_useTimer()" ,asMETHODPR(T, GetUseTimer, (), unsigned), asCALL_THISCALL);
 }
 
+static void ResourceAddMetadata(const String& name, const Variant& value, ResourceWithMetadata* ptr)
+{
+    ptr->AddMetadata(name, value);
+}
+
+static const Variant& ResourceGetMetadata(const String& name, ResourceWithMetadata* ptr)
+{
+    return ptr->GetMetadata(name);
+}
+
+static bool ResourceHasMetadata(ResourceWithMetadata* ptr)
+{
+    return ptr->HasMetadata();
+}
+
+/// Template function for registering a class derived from ResourceWithMetadata.
+template <class T> void RegisterResourceWithMetadata(asIScriptEngine* engine, const char* className)
+{
+    RegisterResource<T>(engine, className);
+    engine->RegisterObjectMethod(className, "void AddMetadata(const String&in, const Variant&in)", asFUNCTION(ResourceAddMetadata), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "void RemoveMetadata(const String&in)", asMETHODPR(T, RemoveMetadata, (const String&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void RemoveAllMetadata()", asMETHOD(T, RemoveAllMetadata), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_metadata(const String&in, const Variant&in)", asFUNCTION(ResourceAddMetadata), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "const Variant& get_metadata(const String&in) const", asFUNCTION(ResourceGetMetadata), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "bool get_hasMetadata() const", asFUNCTION(ResourceHasMetadata), asCALL_CDECL_OBJLAST);
+}
+
 /// Template function for registering a class derived from Drawable.
 template <class T> void RegisterDrawable(asIScriptEngine* engine, const char* className)
 {
@@ -897,7 +924,7 @@ template <class T> void RegisterSoundSource(asIScriptEngine* engine, const char*
 /// Template function for registering a class derived from Texture.
 template <class T> void RegisterTexture(asIScriptEngine* engine, const char* className)
 {
-    RegisterResource<T>(engine, className);
+    RegisterResourceWithMetadata<T>(engine, className);
     RegisterSubclass<Texture, T>(engine, "Texture", className);
     engine->RegisterObjectMethod(className, "void SetNumLevels(uint)", asMETHOD(T, SetNumLevels), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void ClearDataLost()", asMETHODPR(T, ClearDataLost, (), void), asCALL_THISCALL);

+ 1 - 1
Source/Urho3D/AngelScript/AudioAPI.cpp

@@ -33,7 +33,7 @@ namespace Urho3D
 
 void RegisterSound(asIScriptEngine* engine)
 {
-    RegisterResource<Sound>(engine, "Sound");
+    RegisterResourceWithMetadata<Sound>(engine, "Sound");
     engine->RegisterObjectMethod("Sound", "float get_length() const", asMETHOD(Sound, GetLength), asCALL_THISCALL);
     engine->RegisterObjectMethod("Sound", "uint get_sampleSize() const", asMETHOD(Sound, GetSampleSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Sound", "float get_frequency() const", asMETHOD(Sound, GetFrequency), asCALL_THISCALL);

+ 1 - 1
Source/Urho3D/AngelScript/GraphicsAPI.cpp

@@ -1114,7 +1114,7 @@ static void RegisterAnimation(asIScriptEngine* engine)
     engine->RegisterObjectProperty("AnimationTriggerPoint", "float time", offsetof(AnimationTriggerPoint, time_));
     engine->RegisterObjectProperty("AnimationTriggerPoint", "Variant data", offsetof(AnimationTriggerPoint, data_));
 
-    RegisterResource<Animation>(engine, "Animation");
+    RegisterResourceWithMetadata<Animation>(engine, "Animation");
     engine->RegisterObjectMethod("Animation", "AnimationTrack@+ CreateTrack(const String&in)", asMETHOD(Animation, CreateTrack), asCALL_THISCALL);
     engine->RegisterObjectMethod("Animation", "bool RemoveTrack(const String&in)", asMETHOD(Animation, RemoveTrack), asCALL_THISCALL);
     engine->RegisterObjectMethod("Animation", "bool RemoveAllTracks()", asMETHOD(Animation, RemoveAllTracks), asCALL_THISCALL);

+ 3 - 5
Source/Urho3D/Audio/Sound.cpp

@@ -59,7 +59,7 @@ struct WavHeader
 static const unsigned IP_SAFETY = 4;
 
 Sound::Sound(Context* context) :
-    Resource(context),
+    ResourceWithMetadata(context),
     repeat_(0),
     end_(0),
     dataSize_(0),
@@ -348,9 +348,9 @@ void Sound::LoadParameters()
         return;
 
     XMLElement rootElem = file->GetRoot();
-    XMLElement paramElem = rootElem.GetChild();
+    LoadMetadataFromXML(rootElem);
 
-    while (paramElem)
+    for (XMLElement paramElem = rootElem.GetChild(); paramElem; paramElem = paramElem.GetNext())
     {
         String name = paramElem.GetName();
 
@@ -373,8 +373,6 @@ void Sound::LoadParameters()
             if (paramElem.HasAttribute("start") && paramElem.HasAttribute("end"))
                 SetLoop((unsigned)paramElem.GetInt("start"), (unsigned)paramElem.GetInt("end"));
         }
-
-        paramElem = paramElem.GetNext();
     }
 }
 

+ 2 - 2
Source/Urho3D/Audio/Sound.h

@@ -31,9 +31,9 @@ namespace Urho3D
 class SoundStream;
 
 /// %Sound resource.
-class URHO3D_API Sound : public Resource
+class URHO3D_API Sound : public ResourceWithMetadata
 {
-    URHO3D_OBJECT(Sound, Resource);
+    URHO3D_OBJECT(Sound, ResourceWithMetadata);
 
 public:
     /// Construct.

+ 12 - 7
Source/Urho3D/Graphics/Animation.cpp

@@ -107,7 +107,7 @@ void AnimationTrack::GetKeyFrameIndex(float time, unsigned& index) const
 }
 
 Animation::Animation(Context* context) :
-    Resource(context),
+    ResourceWithMetadata(context),
     length_(0.f)
 {
 }
@@ -173,17 +173,16 @@ bool Animation::BeginLoad(Deserializer& source)
     if (file)
     {
         XMLElement rootElem = file->GetRoot();
-        XMLElement triggerElem = rootElem.GetChild("trigger");
-        while (triggerElem)
+        for (XMLElement triggerElem = rootElem.GetChild("trigger"); triggerElem; triggerElem = triggerElem.GetNext("trigger"))
         {
             if (triggerElem.HasAttribute("normalizedtime"))
                 AddTrigger(triggerElem.GetFloat("normalizedtime"), true, triggerElem.GetVariant());
             else if (triggerElem.HasAttribute("time"))
                 AddTrigger(triggerElem.GetFloat("time"), false, triggerElem.GetVariant());
-
-            triggerElem = triggerElem.GetNext("trigger");
         }
 
+        LoadMetadataFromXML(rootElem);
+
         memoryUse += triggers_.Size() * sizeof(AnimationTriggerPoint);
         SetMemoryUse(memoryUse);
         return true;
@@ -196,7 +195,7 @@ bool Animation::BeginLoad(Deserializer& source)
     if (jsonFile)
     {
         const JSONValue& rootVal = jsonFile->GetRoot();
-        JSONArray triggerArray = rootVal.Get("triggers").GetArray();
+        const JSONArray& triggerArray = rootVal.Get("triggers").GetArray();
 
         for (unsigned i = 0; i < triggerArray.Size(); i++)
         {
@@ -212,6 +211,9 @@ bool Animation::BeginLoad(Deserializer& source)
             }
         }
 
+        const JSONArray& metadataArray = rootVal.Get("metadata").GetArray();
+        LoadMetadataFromJSON(metadataArray);
+
         memoryUse += triggers_.Size() * sizeof(AnimationTriggerPoint);
         SetMemoryUse(memoryUse);
         return true;
@@ -252,7 +254,7 @@ bool Animation::Save(Serializer& dest) const
     }
 
     // If triggers have been defined, write an XML file for them
-    if (triggers_.Size())
+    if (!triggers_.Empty() || HasMetadata())
     {
         File* destFile = dynamic_cast<File*>(&dest);
         if (destFile)
@@ -269,6 +271,8 @@ bool Animation::Save(Serializer& dest) const
                 triggerElem.SetVariant(triggers_[i].data_);
             }
 
+            SaveMetadataToXML(rootElem);
+
             File xmlFile(context_, xmlName, FILE_WRITE);
             xml->Save(xmlFile);
         }
@@ -373,6 +377,7 @@ SharedPtr<Animation> Animation::Clone(const String& cloneName) const
     ret->length_ = length_;
     ret->tracks_ = tracks_;
     ret->triggers_ = triggers_;
+    ret->CopyMetadata(*this);
     ret->SetMemoryUse(GetMemoryUse());
     
     return ret;

+ 2 - 2
Source/Urho3D/Graphics/Animation.h

@@ -107,9 +107,9 @@ static const unsigned char CHANNEL_ROTATION = 0x2;
 static const unsigned char CHANNEL_SCALE = 0x4;
 
 /// Skeletal animation resource.
-class URHO3D_API Animation : public Resource
+class URHO3D_API Animation : public ResourceWithMetadata
 {
-    URHO3D_OBJECT(Animation, Resource);
+    URHO3D_OBJECT(Animation, ResourceWithMetadata);
 
 public:
     /// Construct.

+ 3 - 5
Source/Urho3D/Graphics/Texture.cpp

@@ -56,7 +56,7 @@ static const char* filterModeNames[] =
 };
 
 Texture::Texture(Context* context) :
-    Resource(context),
+    ResourceWithMetadata(context),
     GPUObject(GetSubsystem<Graphics>()),
     shaderResourceView_(0),
     sampler_(0),
@@ -204,8 +204,8 @@ void Texture::SetParameters(XMLFile* file)
 
 void Texture::SetParameters(const XMLElement& element)
 {
-    XMLElement paramElem = element.GetChild();
-    while (paramElem)
+    LoadMetadataFromXML(element);
+    for (XMLElement paramElem = element.GetChild(); paramElem; paramElem = paramElem.GetNext())
     {
         String name = paramElem.GetName();
 
@@ -248,8 +248,6 @@ void Texture::SetParameters(const XMLElement& element)
 
         if (name == "srgb")
             SetSRGB(paramElem.GetBool("enable"));
-
-        paramElem = paramElem.GetNext();
     }
 }
 

+ 1 - 1
Source/Urho3D/Graphics/Texture.h

@@ -36,7 +36,7 @@ class XMLElement;
 class XMLFile;
 
 /// Base class for texture resources.
-class URHO3D_API Texture : public Resource, public GPUObject
+class URHO3D_API Texture : public ResourceWithMetadata, public GPUObject
 {
 public:
     /// Construct.

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/Audio/Sound.pkg

@@ -1,7 +1,7 @@
 $#include "IO/File.h"
 $#include "Audio/Sound.h"
 
-class Sound : public Resource
+class Sound : public ResourceWithMetadata
 {
     Sound();
     ~Sound();

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/Graphics/Animation.pkg

@@ -38,7 +38,7 @@ struct AnimationTriggerPoint
     Variant data_ @ data;
 };
 
-class Animation : public Resource
+class Animation : public ResourceWithMetadata
 {
     Animation();
     ~Animation();

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/Graphics/Texture.pkg

@@ -1,6 +1,6 @@
 $#include "Graphics/Texture.h"
 
-class Texture : public Resource
+class Texture : public ResourceWithMetadata
 {
     void SetNumLevels(unsigned levels);
     void SetFilterMode(TextureFilterMode filter);

+ 9 - 0
Source/Urho3D/LuaScript/pkgs/Resource/Resource.pkg

@@ -16,6 +16,15 @@ class Resource
     tolua_readonly tolua_property__get_set unsigned memoryUse;
 };
 
+class ResourceWithMetadata : public Resource
+{
+    void AddMetadata(const String& name, const Variant& value);
+    void RemoveMetadata(const String& name);
+    void RemoveAllMetadata();
+    const Variant& GetMetadata(const String& name) const;
+    bool HasMetadata() const;
+}
+
 ${
 static bool ResourceLoad(Resource* resource, const String& fileName)
 {

+ 63 - 0
Source/Urho3D/Resource/Resource.cpp

@@ -26,6 +26,7 @@
 #include "../IO/File.h"
 #include "../IO/Log.h"
 #include "../Resource/Resource.h"
+#include "../Resource/XMLElement.h"
 
 namespace Urho3D
 {
@@ -128,4 +129,66 @@ unsigned Resource::GetUseTimer()
         return useTimer_.GetMSec(false);
 }
 
+void ResourceWithMetadata::AddMetadata(const String& name, const Variant& value)
+{
+    bool exists;
+    metadata_.Insert(MakePair(StringHash(name), value), exists);
+    if (!exists)
+        metadataKeys_.Push(name);
+}
+
+void ResourceWithMetadata::RemoveMetadata(const String& name)
+{
+    metadata_.Erase(name);
+    metadataKeys_.Remove(name);
+}
+
+void ResourceWithMetadata::RemoveAllMetadata()
+{
+    metadata_.Clear();
+    metadataKeys_.Clear();
+}
+
+const Urho3D::Variant& ResourceWithMetadata::GetMetadata(const String& name) const
+{
+    const Variant* value = metadata_[name];
+    return value ? *value : Variant::EMPTY;
+}
+
+bool ResourceWithMetadata::HasMetadata() const
+{
+    return !metadata_.Empty();
+}
+
+void ResourceWithMetadata::LoadMetadataFromXML(const XMLElement& source)
+{
+    for (XMLElement elem = source.GetChild("metadata"); elem; elem = elem.GetNext("metadata"))
+        AddMetadata(elem.GetAttribute("name"), elem.GetVariant());
+}
+
+void ResourceWithMetadata::LoadMetadataFromJSON(const JSONArray& array)
+{
+    for (unsigned i = 0; i < array.Size(); i++)
+    {
+        const JSONValue& value = array.At(i);
+        AddMetadata(value.Get("name").GetString(), value.GetVariant());
+    }
+}
+
+void ResourceWithMetadata::SaveMetadataToXML(XMLElement& destination) const
+{
+    for (unsigned i = 0; i < metadataKeys_.Size(); ++i)
+    {
+        XMLElement elem = destination.CreateChild("metadata");
+        elem.SetString("name", metadataKeys_[i]);
+        elem.SetVariant(GetMetadata(metadataKeys_[i]));
+    }
+}
+
+void ResourceWithMetadata::CopyMetadata(const ResourceWithMetadata& source)
+{
+    metadata_ = source.metadata_;
+    metadataKeys_ = source.metadataKeys_;
+}
+
 }

+ 39 - 0
Source/Urho3D/Resource/Resource.h

@@ -24,12 +24,14 @@
 
 #include "../Core/Object.h"
 #include "../Core/Timer.h"
+#include "../Resource/JSONValue.h"
 
 namespace Urho3D
 {
 
 class Deserializer;
 class Serializer;
+class XMLElement;
 
 /// Asynchronous loading state of a resource.
 enum AsyncLoadState
@@ -106,6 +108,43 @@ private:
     AsyncLoadState asyncLoadState_;
 };
 
+/// Base class for resources that support arbitrary metadata stored. Metadata serialization shall be implemented in derived classes.
+class URHO3D_API ResourceWithMetadata : public Resource
+{
+    URHO3D_OBJECT(ResourceWithMetadata, Resource);
+
+public:
+    /// Construct.
+    ResourceWithMetadata(Context* context) : Resource(context) {}
+
+    /// Add new metadata variable or overwrite old value.
+    void AddMetadata(const String& name, const Variant& value);
+    /// Remove metadata variable.
+    void RemoveMetadata(const String& name);
+    /// Remove all metadata variables.
+    void RemoveAllMetadata();
+    /// Return metadata variable.
+    const Variant& GetMetadata(const String& name) const;
+    /// Return whether the resource has metadata.
+    bool HasMetadata() const;
+
+protected:
+    /// Load metadata from <metadata> children of XML element.
+    void LoadMetadataFromXML(const XMLElement& source);
+    /// Load metadata from JSON array.
+    void LoadMetadataFromJSON(const JSONArray& array);
+    /// Save as <metadata> children of XML element.
+    void SaveMetadataToXML(XMLElement& destination) const;
+    /// Copy metadata from another resource.
+    void CopyMetadata(const ResourceWithMetadata& source);
+
+private:
+    /// Animation metadata variables.
+    VariantMap metadata_;
+    /// Animation metadata keys.
+    StringVector metadataKeys_;
+};
+
 inline const String& GetResourceName(Resource* resource)
 {
     return resource ? resource->GetName() : String::EMPTY;