Browse Source

Merge pull request #1104 from AtomicGameEngine/JME-ATOMIC-CACHEMAP

Resource to Cache mapping at runtime, consistent StringHash C# mapping
JoshEngebretson 9 years ago
parent
commit
81800d1239

+ 70 - 0
Script/AtomicNET/AtomicNET/Math/StringHash.cs

@@ -0,0 +1,70 @@
+//
+// Copyright (c) 2015 Xamarin Inc
+// Copyright (c) 2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine {
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StringHash {
+        public uint Code;
+        public StringHash (uint code)
+        {
+            this.Code = code;
+        }
+
+        public StringHash (string str)
+        {
+            this.Code = csi_Atomic_AtomicNET_StringToStringHash (str);
+        }
+
+        public static implicit operator uint(StringHash hash)
+        {
+            return hash.Code;
+        }
+
+        public static implicit operator StringHash(uint code)
+        {
+            return new StringHash(code);
+        }
+
+        public static implicit operator StringHash(string str)
+        {
+            return new StringHash(str);
+        }
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        extern static uint csi_Atomic_AtomicNET_StringToStringHash (string str);
+
+        public override string ToString ()
+        {
+            return $"StringHash({Code:x})";
+        }
+
+        public static StringHash Zero = new StringHash(0);
+    }
+
+
+
+}

+ 14 - 8
Source/Atomic/Resource/ResourceCache.cpp

@@ -381,11 +381,11 @@ bool ResourceCache::ReloadResource(Resource* resource)
 
     resource->SendEvent(E_RELOADSTARTED);
 
-    bool success = false;
-    SharedPtr<File> file = GetFile(resource->GetName());
-
 // ATOMIC BEGIN
 
+    bool success = false;
+    SharedPtr<File> file = GetFile(resource->GetName(), true, resource->GetType());
+
     if (file)
     {
 #ifdef ATOMIC_PLATFORM_DESKTOP
@@ -513,17 +513,18 @@ void ResourceCache::RemoveResourceRouter(ResourceRouter* router)
         }
     }
 }
-
-SharedPtr<File> ResourceCache::GetFile(const String& nameIn, bool sendEventOnFailure)
+// ATOMIC BEGIN
+SharedPtr<File> ResourceCache::GetFile(const String& nameIn, bool sendEventOnFailure, StringHash type)
 {
     MutexLock lock(resourceMutex_);
 
     String name = SanitateResourceName(nameIn);
+
     if (!isRouting_)
     {
         isRouting_ = true;
         for (unsigned i = 0; i < resourceRouters_.Size(); ++i)
-            resourceRouters_[i]->Route(name, RESOURCE_GETFILE);
+            resourceRouters_[i]->Route(name, type, RESOURCE_GETFILE);
         isRouting_ = false;
     }
 
@@ -567,6 +568,7 @@ SharedPtr<File> ResourceCache::GetFile(const String& nameIn, bool sendEventOnFai
 
     return SharedPtr<File>();
 }
+// ATOMIC END
 
 Resource* ResourceCache::GetExistingResource(StringHash type, const String& nameIn)
 {
@@ -632,10 +634,12 @@ Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool
         return 0;
     }
 
+    // ATOMIC BEGIN
     // Attempt to load the resource
-    SharedPtr<File> file = GetFile(name, sendEventOnFailure);
+    SharedPtr<File> file = GetFile(name, sendEventOnFailure, type);
     if (!file)
         return 0;   // Error is already logged
+    // ATOMIC END
 
     ATOMIC_LOGDEBUG("Loading resource " + name);
     resource->SetName(name);
@@ -785,14 +789,16 @@ bool ResourceCache::Exists(const String& nameIn) const
 {
     MutexLock lock(resourceMutex_);
 
+    // ATOMIC BEGIN
     String name = SanitateResourceName(nameIn);
     if (!isRouting_)
     {
         isRouting_ = true;
         for (unsigned i = 0; i < resourceRouters_.Size(); ++i)
-            resourceRouters_[i]->Route(name, RESOURCE_CHECKEXISTS);
+            resourceRouters_[i]->Route(name, StringHash::ZERO, RESOURCE_CHECKEXISTS);
         isRouting_ = false;
     }
+    // ATOMIC END
 
     if (name.Empty())
         return false;

+ 8 - 2
Source/Atomic/Resource/ResourceCache.h

@@ -63,9 +63,12 @@ enum ResourceRequest
     RESOURCE_GETFILE = 1
 };
 
+// ATOMIC BEGIN
 /// Optional resource request processor. Can deny requests, re-route resource file names, or perform other processing per request.
 class ATOMIC_API ResourceRouter : public Object
 {
+    ATOMIC_OBJECT(ResourceRouter, Object);
+
 public:
     /// Construct.
     ResourceRouter(Context* context) :
@@ -74,8 +77,9 @@ public:
     }
 
     /// Process the resource request and optionally modify the resource name string. Empty name string means the resource is not found or not allowed.
-    virtual void Route(String& name, ResourceRequest requestType) = 0;
+    virtual void Route(String& name, StringHash type, ResourceRequest requestType) = 0;
 };
+// ATOMIC END
 
 /// %Resource cache subsystem. Loads resources on demand and stores them for later access.
 class ATOMIC_API ResourceCache : public Object
@@ -134,8 +138,10 @@ public:
     /// Remove a resource router object.
     void RemoveResourceRouter(ResourceRouter* router);
 
+    // ATOMIC BEGIN
     /// Open and return a file from the resource load paths or from inside a package file. If not found, use a fallback search with absolute path. Return null if fails. Can be called from outside the main thread.
-    SharedPtr<File> GetFile(const String& name, bool sendEventOnFailure = true);
+    SharedPtr<File> GetFile(const String& name, bool sendEventOnFailure = true, StringHash type = StringHash::ZERO);
+    // ATOMIC END
     /// Return a resource by type and name. Load if not loaded yet. Return null if not found or if fails, unless SetReturnFailedResources(true) has been called. Can be called only from the main thread.
     Resource* GetResource(StringHash type, const String& name, bool sendEventOnFailure = true);
     /// Load a resource without storing it in the resource cache. Return null if not found or if fails. Can be called from outside the main thread if the resource itself is safe to load completely (it does not possess for example GPU data.)

+ 98 - 0
Source/Atomic/Resource/ResourceMapRouter.cpp

@@ -0,0 +1,98 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include <Atomic/IO/Log.h>
+#include "JSONValue.h"
+#include "JSONFile.h"
+#include "ResourceCache.h"
+#include "ResourceMapRouter.h"
+
+namespace Atomic
+{
+
+ResourceMapRouter::ResourceMapRouter(Context* context, const String& mapFile) : ResourceRouter(context)
+{
+    if (mapFile.Length())
+    {
+        ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+        if (!cache)
+            return;
+
+        SharedPtr<JSONFile> jsonFile = cache->GetTempResource<JSONFile>(mapFile);
+
+        if (jsonFile.NotNull())
+        {
+            if (Load(jsonFile->GetRoot()))
+                cache->AddResourceRouter(this);
+        }
+
+    }
+}
+
+bool ResourceMapRouter::Load(const JSONValue& json)
+{
+    const JSONValue& assetMap = json.Get("assetMap");
+
+    if (!assetMap.IsObject())
+        return false;
+
+    ConstJSONObjectIterator itr = assetMap.Begin();
+    while (itr != assetMap.End())
+    {
+        StringVector tags = itr->first_.Split(';');
+
+        if (tags.Size() == 2)
+        {
+           resourceMap_[tags[0]][tags[1]] = itr->second_.GetString();
+        }
+
+        itr++;
+    }
+
+    return true;
+}
+
+void ResourceMapRouter::Route(String& name, StringHash type, ResourceRequest requestType)
+{
+
+    if (type == StringHash::ZERO)
+        return;
+
+    HashMap<StringHash, HashMap<StringHash, String>>::ConstIterator itr = resourceMap_.Find(type);
+    if (itr == resourceMap_.End())
+    {
+        return;
+    }
+
+    HashMap<StringHash, String>::ConstIterator mitr = itr->second_.Find(name);
+    if (mitr == itr->second_.End())
+    {
+        return;
+    }
+
+
+    name = mitr->second_;
+
+}
+
+}

+ 51 - 0
Source/Atomic/Resource/ResourceMapRouter.h

@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "ResourceCache.h"
+
+namespace Atomic
+{
+
+class JSONValue;
+
+// Resource router which maps resource requests to processed cache files, different files can be mapped based on type of request
+class ATOMIC_API ResourceMapRouter : public ResourceRouter
+{
+    ATOMIC_OBJECT(ResourceMapRouter, ResourceRouter)
+
+public:
+    /// Construct.
+    ResourceMapRouter(Context* context, const String& mapFile = String::EMPTY);
+
+    void Route(String& name, StringHash type, ResourceRequest requestType);
+
+private:
+
+    bool Load(const JSONValue& json);
+
+    // Type -> FromPath (hash) -> CachePath
+    HashMap<StringHash, HashMap<StringHash, String>> resourceMap_;
+};
+
+}

+ 4 - 0
Source/AtomicApp/Player/PlayerApp.cpp

@@ -24,6 +24,7 @@
 #include <Atomic/Input/InputEvents.h>
 #include <Atomic/Engine/Engine.h>
 #include <Atomic/Graphics/Graphics.h>
+#include <Atomic/Resource/ResourceMapRouter.h>
 #include <Atomic/UI/UI.h>
 #include <AtomicJS/Javascript/Javascript.h>
 
@@ -98,6 +99,9 @@ namespace Atomic
 
     void PlayerApp::Start()
     {
+        // Initialize resource mapper
+        SharedPtr<ResourceMapRouter> router(new ResourceMapRouter(context_, "__atomic_ResourceCacheMap.json"));
+
         UI* ui = GetSubsystem<UI>();
         ui->Initialize("DefaultUI/language/lng_en.tb.txt");
         ui->LoadDefaultPlayerSkin();

+ 5 - 0
Source/ToolCore/Assets/Asset.h

@@ -55,6 +55,8 @@ public:
 
     const String& GetGUID() const { return guid_; }
     const String& GetName() const { return name_; }
+
+    // Get absolute path to asset file
     const String& GetPath() const { return path_; }
 
     String GetExtension() const;
@@ -101,6 +103,9 @@ public:
     /// Instantiate a node from the asset
     Node* InstantiateNode(Node* parent, const String& name);
 
+    // Get a mapping of the assets path to cache file representations, by type
+    void GetAssetCacheMap(HashMap<String, String>& assetMap) { if (importer_.NotNull()) importer_->GetAssetCacheMap(assetMap); }
+
 
 private:
 

+ 60 - 2
Source/ToolCore/Assets/AssetDatabase.cpp

@@ -43,7 +43,8 @@ namespace ToolCore
 {
 
 AssetDatabase::AssetDatabase(Context* context) : Object(context),
-    assetScanDepth_(0)
+    assetScanDepth_(0),
+    assetScanImport_(false)
 {
     SubscribeToEvent(E_LOADFAILED, ATOMIC_HANDLER(AssetDatabase, HandleResourceLoadFailed));
     SubscribeToEvent(E_PROJECTLOADED, ATOMIC_HANDLER(AssetDatabase, HandleProjectLoaded));
@@ -298,6 +299,7 @@ bool AssetDatabase::ImportDirtyAssets()
 
     for (unsigned i = 0; i < assets.Size(); i++)
     {
+        assetScanImport_ = true;
         assets[i]->Import();
         assets[i]->Save();
         assets[i]->dirty_ = false;
@@ -321,11 +323,66 @@ void AssetDatabase::PreloadAssets()
 
 }
 
+void AssetDatabase::UpdateAssetCacheMap()
+{
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+    if (project_.Null())
+        return;
+
+    bool gen = assetScanImport_;
+    assetScanImport_ = false;
+
+    String cachepath = project_->GetProjectPath() + "Cache/__atomic_ResourceCacheMap.json";
+
+    if (!gen && !fileSystem->FileExists(cachepath))
+        gen = true;
+
+    if (!gen)
+        return;
+
+    List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
+
+    HashMap<String, String> assetMap;
+    JSONValue jAssetMap;
+
+    while (itr != assets_.End())
+    {
+        assetMap.Clear();
+        (*itr)->GetAssetCacheMap(assetMap);
+
+        HashMap<String, String>::ConstIterator amitr = assetMap.Begin();
+
+        while (amitr != assetMap.End())
+        {
+            jAssetMap.Set(amitr->first_, amitr->second_);
+            amitr++;
+        }
+
+        itr++;
+    }
+
+    SharedPtr<File> file(new File(context_, cachepath, FILE_WRITE));
+    if (!file->IsOpen())
+    {
+        ATOMIC_LOGERRORF("Unable to update ResourceCacheMap: %s", cachepath.CString());
+        return;
+    }
+
+
+    SharedPtr<JSONFile> jsonFile(new JSONFile(context_));
+    jsonFile->GetRoot().Set("assetMap", jAssetMap);
+
+    jsonFile->Save(*file);
+
+
+}
 
 void AssetDatabase::Scan()
 {
     if (!assetScanDepth_)
     {
+        assert(!assetScanImport_);
         SendEvent(E_ASSETSCANBEGIN);
     }
 
@@ -410,7 +467,8 @@ void AssetDatabase::Scan()
 
     if (!assetScanDepth_)
     {
-        SendEvent(E_ASSETSCANEND);
+        UpdateAssetCacheMap();
+        SendEvent(E_ASSETSCANEND);        
     }
 }
 

+ 5 - 0
Source/ToolCore/Assets/AssetDatabase.h

@@ -85,6 +85,9 @@ private:
     bool ImportDirtyAssets();
     void PreloadAssets();
 
+    // Update mapping of asset paths to cache file representations, by type
+    void UpdateAssetCacheMap();
+
     SharedPtr<Project> project_;
     List<SharedPtr<Asset>> assets_;
 
@@ -96,6 +99,8 @@ private:
     Vector<String> usedGUID_;
 
     unsigned assetScanDepth_;
+    // Whether any asset was imported during scan
+    bool assetScanImport_;
 
 };
 

+ 3 - 0
Source/ToolCore/Assets/AssetImporter.h

@@ -68,6 +68,9 @@ public:
 
 protected:
 
+    // Get a mapping of the assets path to cache file representations, by type
+    virtual void GetAssetCacheMap(HashMap<String, String>& assetMap) {}
+
     virtual bool Import() { return true; }
 
     WeakPtr<Asset> asset_;

+ 24 - 0
Source/ToolCore/Assets/ModelImporter.cpp

@@ -336,6 +336,30 @@ void ModelImporter::GetAnimations(PODVector<Animation*>& animations)
 
 }
 
+void ModelImporter::GetAssetCacheMap(HashMap<String, String>& assetMap)
+{
+    if (asset_.Null())
+        return;
+
+    String assetPath = asset_->GetRelativePath().ToLower();
+
+    String cachePath = asset_->GetGUID().ToLower();
+
+    // Default is load node xml
+    assetMap["Node;" + assetPath] = cachePath;
+    assetMap["Model;" + assetPath] = cachePath + ".mdl";
+
+    PODVector<Animation*> animations;
+
+    GetAnimations(animations);
+
+    for (unsigned i = 0; i < animations.Size(); i++)
+    {
+        Animation* anim = animations[i];
+        assetMap["Animation;" + anim->GetAnimationName().ToLower() + "@" + assetPath] = cachePath + "_" + anim->GetAnimationName() + ".ani";
+    }
+}
+
 bool ModelImporter::LoadSettingsInternal(JSONValue& jsonRoot)
 {
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))

+ 1 - 0
Source/ToolCore/Assets/ModelImporter.h

@@ -107,6 +107,7 @@ protected:
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool SaveSettingsInternal(JSONValue& jsonRoot);
 
+    void GetAssetCacheMap(HashMap<String, String>& assetMap);
 
     double scale_;
     bool importAnimations_;

+ 19 - 3
Source/ToolCore/JSBind/CSharp/CSFunctionWriter.cpp

@@ -87,10 +87,14 @@ void CSFunctionWriter::GenNativeCallParameters(String& sig)
             }
             else
             {
-                if (ptype->type_->asStringType() || ptype->type_->asStringHashType())
+                if (ptype->type_->asStringType())
                 {
                     args.Push(ToString("%s ? String(%s) : String::EMPTY", ptype->name_.CString(), ptype->name_.CString()));
                 }
+                else if (ptype->type_->asStringHashType())
+                {
+                    args.Push(ToString("StringHash(%s)", ptype->name_.CString()));
+                }
                 else
                 {
                     args.Push(ToString("%s", ptype->name_.CString()));
@@ -732,10 +736,14 @@ void CSFunctionWriter::WriteManagedFunction(String& source)
     if (function_->GetReturnType())
     {
 
-        if (function_->GetReturnType()->type_->asStringType() || function_->GetReturnType()->type_->asStringHashType())
+        if (function_->GetReturnType()->type_->asStringType())
         {
             line += "return System.Runtime.InteropServices.Marshal.PtrToStringAnsi(";
         }
+        else if (function_->GetReturnType()->type_->asStringHashType())
+        {
+            line += "return ";
+        }
         else if (function_->GetReturnType()->type_->asVectorType())
         {
             source += IndentLine(ToString("var returnScriptVector = %s%s%uReturnValue.GetScriptVector();\n", klass->GetName().CString(), function_->GetName().CString(), function_->GetID()));
@@ -774,7 +782,7 @@ void CSFunctionWriter::WriteManagedFunction(String& source)
 
     if (function_->GetReturnType())
     {
-        if (function_->GetReturnType()->type_->asStringType() || function_->GetReturnType()->type_->asStringHashType())
+        if (function_->GetReturnType()->type_->asStringType())
             line += ")";
     }
 
@@ -1006,6 +1014,14 @@ String CSFunctionWriter::MapDefaultParameter(JSBFunctionType* parameter)
         return "default(Quaternion)";
     }
 
+    if (init == "StringHash::ZERO")
+    {
+        dparm.type = "StringHash";
+        dparm.assignment = "StringHash.Zero";
+        defaultStructParameters_.Push(dparm);
+        return "default(StringHash)";
+    }
+
     return String::EMPTY;
 }
 

+ 11 - 3
Source/ToolCore/JSBind/CSharp/CSTypeHelper.cpp

@@ -179,10 +179,14 @@ String CSTypeHelper::GetManagedTypeString(JSBType* type)
         JSBClassType* classType = type->asClassType();
         value = classType->class_->GetName();
     }
-    else if (type->asStringType() || type->asStringHashType())
+    else if (type->asStringType())
     {
         value = "string";
     }
+    else if (type->asStringHashType())
+    {
+        value = "StringHash";
+    }
     else if (type->asEnumType())
     {
         value = type->asEnumType()->enum_->GetName();
@@ -256,7 +260,7 @@ String CSTypeHelper::GetNativeTypeString(JSBType* type)
     }
     else if (type->asStringHashType())
     {
-        value = "const char*";
+        value = "unsigned";
     }
     else if (type->asEnumType())
     {
@@ -297,10 +301,14 @@ String CSTypeHelper::GetPInvokeTypeString(JSBType* type)
         else
             value = "IntPtr";
     }
-    else if (type->asStringType() || type->asStringHashType())
+    else if (type->asStringType())
     {
         value = "string";
     }
+    else if (type->asStringHashType())
+    {
+        value = "uint";
+    }
     else if (type->asEnumType())
     {
         value = type->asEnumType()->enum_->GetName();