• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <core/intf_engine.h>
17 #include <render/intf_render_context.h>
18 #include <util/io_util.h>
19 #include <util/json.h>
20 #include <util/path_util.h>
21 
22 #include <ecs_serializer/api.h>
23 #include <ecs_serializer/intf_ecs_asset_manager.h>
24 
25 using namespace BASE_NS;
26 using namespace CORE_NS;
27 using namespace RENDER_NS;
28 using namespace CORE3D_NS;
29 using namespace UTIL_NS;
30 
31 ECS_SERIALIZER_BEGIN_NAMESPACE()
32 
33 class EcsAssetManager : public IEcsAssetManager {
34 public:
35     explicit EcsAssetManager(CORE3D_NS::IGraphicsContext& graphicsContext);
36     virtual ~EcsAssetManager();
37     // From IEcsAssetManager
38     ExtensionType GetExtensionType(BASE_NS::string_view ext) const override;
39 
40     IEcsAssetLoader::Ptr CreateEcsAssetLoader(
41         IEntityCollection& ec, BASE_NS::string_view src, BASE_NS::string_view contextUri) override;
42 
43     bool LoadAsset(IEntityCollection& ec, BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
44     bool SaveJsonEntityCollection(
45         const IEntityCollection& ec, BASE_NS::string_view uri, BASE_NS::string_view contextUri) const override;
46 
47     IEntityCollection* LoadAssetToCache(CORE_NS::IEcs& ecs, BASE_NS::string_view cacheUri, BASE_NS::string_view uri,
48         BASE_NS::string_view contextUri, bool active, bool forceReload) override;
49 
50     bool IsCachedCollection(BASE_NS::string_view uri, BASE_NS::string_view contextUri) const override;
51     IEntityCollection* CreateCachedCollection(
52         CORE_NS::IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
53     IEntityCollection* GetCachedCollection(BASE_NS::string_view uri, BASE_NS::string_view contextUri) const override;
54     void RemoveFromCache(BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
55     void ClearCache() override;
56 
57     void RefreshAsset(IEntityCollection& ec, bool active) override;
58 
59     IEntityCollection* InstantiateCollection(
60         IEntityCollection& ec, BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
61 
62     IEcsSerializer& GetEcsSerializer() override;
63     const IEcsSerializer& GetEcsSerializer() const override;
64     // From IEcsSerializer::IListener
65     IEntityCollection* GetExternalCollection(
66         CORE_NS::IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
67 
68 protected:
69     void Destroy() override;
70 
71 private:
72     RENDER_NS::IRenderContext& renderContext_;
73     CORE3D_NS::IGraphicsContext& graphicsContext_;
74     IEcsSerializer::Ptr ecsSerializer_;
75 
76     // Mapping from uri to a collection of entities. The entities live in the ECS but we need to keep track of some kind
77     // of internal ownership of each cached entity.
78     BASE_NS::unordered_map<BASE_NS::string, IEntityCollection::Ptr> cacheCollections_;
79 };
80 
EcsAssetManager(IGraphicsContext & graphicsContext)81 EcsAssetManager::EcsAssetManager(IGraphicsContext& graphicsContext)
82     : renderContext_(graphicsContext.GetRenderContext()), graphicsContext_(graphicsContext),
83       ecsSerializer_(CreateEcsSerializer(renderContext_))
84 {
85     ecsSerializer_->SetDefaultSerializers();
86     ecsSerializer_->SetListener(this);
87 }
88 
~EcsAssetManager()89 EcsAssetManager::~EcsAssetManager() {}
90 
GetExtensionType(string_view ext) const91 EcsAssetManager::ExtensionType EcsAssetManager::GetExtensionType(string_view ext) const
92 {
93     // Ignore case.
94     // Better type recognition
95     if (ext == "collection") {
96         return ExtensionType::COLLECTION;
97     } else if (ext == "scene") {
98         return ExtensionType::SCENE;
99     } else if (ext == "prefab") {
100         return ExtensionType::PREFAB;
101     } else if (ext == "animation") {
102         return ExtensionType::ANIMATION;
103     } else if (ext == "material") {
104         return ExtensionType::MATERIAL;
105     } else if (ext == "gltf") {
106         return ExtensionType::GLTF;
107     } else if (ext == "glb") {
108         return ExtensionType::GLB;
109     } else if (ext == "png") {
110         return ExtensionType::PNG;
111     } else if (ext == "jpg" || ext == "jpeg") {
112         return ExtensionType::JPG;
113     } else if (ext == "ktx") {
114         return ExtensionType::KTX;
115     } else {
116         return ExtensionType::NOT_SUPPORTED;
117     }
118 }
119 
CreateEcsAssetLoader(IEntityCollection & ec,string_view src,string_view contextUri)120 IEcsAssetLoader::Ptr EcsAssetManager::CreateEcsAssetLoader(
121     IEntityCollection& ec, string_view src, string_view contextUri)
122 {
123     return ::ECS_SERIALIZER_NS::CreateEcsAssetLoader(*this, graphicsContext_, ec, src, contextUri);
124 }
125 
LoadAsset(IEntityCollection & ec,string_view uri,string_view contextUri)126 bool EcsAssetManager::LoadAsset(IEntityCollection& ec, string_view uri, string_view contextUri)
127 {
128     auto assetLoader = CreateEcsAssetLoader(ec, uri, contextUri);
129     if (assetLoader) {
130         //  Use syncronous asset loading.
131         assetLoader->LoadAsset();
132         auto result = assetLoader->GetResult();
133         return result.success;
134     }
135     return false;
136 }
137 
SaveJsonEntityCollection(const IEntityCollection & ec,string_view uri,string_view contextUri) const138 bool EcsAssetManager::SaveJsonEntityCollection(
139     const IEntityCollection& ec, string_view uri, string_view contextUri) const
140 {
141     const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
142     CORE_LOG_D("SaveJsonEntityCollection: '%s'", resolvedUri.c_str());
143 
144     json::standalone_value jsonOut;
145     auto result = GetEcsSerializer().WriteEntityCollection(ec, jsonOut);
146     if (result) {
147         const string jsonString = to_formatted_string(jsonOut, 4);
148         auto& fileManager = renderContext_.GetEngine().GetFileManager();
149         if (IoUtil::SaveTextFile(fileManager, resolvedUri, { jsonString.data(), jsonString.size() })) {
150             return true;
151         }
152     }
153     return false;
154 }
155 
LoadAssetToCache(IEcs & ecs,string_view cacheUri,string_view uri,string_view contextUri,bool active,bool forceReload)156 IEntityCollection* EcsAssetManager::LoadAssetToCache(
157     IEcs& ecs, string_view cacheUri, string_view uri, string_view contextUri, bool active, bool forceReload)
158 {
159     // Check if this collection was already loaded.
160     const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
161     const auto& cacheId = cacheUri;
162 
163     if (!forceReload) {
164         auto it = cacheCollections_.find(cacheId);
165         if (it != cacheCollections_.cend()) {
166             return it->second.get();
167         }
168     }
169 
170     // Not yet loaded (or forcing reload) -> load the entity collection.
171     // Need to first remove the possible cached collection because otherwise gltf importer will not reload the resources
172     // like meshes if they have the same names.
173     cacheCollections_.erase(cacheId);
174 
175     auto ec = CreateEntityCollection(ecs, cacheUri, contextUri);
176     if (LoadAsset(*ec, resolvedUri, contextUri)) {
177         // Something was loaded. Set all loaded entities as inactive if requested.
178         if (!active) {
179             ec->SetActive(false);
180         }
181         // Add loaded collection to cache map.
182         return (cacheCollections_[cacheId] = move(ec)).get();
183     } else {
184         return nullptr;
185     }
186 }
187 
IsCachedCollection(string_view uri,string_view contextUri) const188 bool EcsAssetManager::IsCachedCollection(string_view uri, string_view contextUri) const
189 {
190     const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
191     const auto& cacheId = resolvedUri;
192 
193     return cacheCollections_.find(cacheId) != cacheCollections_.cend();
194 }
195 
CreateCachedCollection(IEcs & ecs,BASE_NS::string_view uri,BASE_NS::string_view contextUri)196 IEntityCollection* EcsAssetManager::CreateCachedCollection(
197     IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri)
198 {
199     auto ec = CreateEntityCollection(ecs, uri, contextUri);
200     ec->SetActive(false);
201 
202     const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
203     const auto& cacheId = resolvedUri;
204 
205     // Add the created collection to the cache map.
206     return (cacheCollections_[cacheId] = move(ec)).get();
207 }
208 
GetCachedCollection(string_view uri,string_view contextUri) const209 IEntityCollection* EcsAssetManager::GetCachedCollection(string_view uri, string_view contextUri) const
210 {
211     const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
212     const auto& cacheId = resolvedUri;
213 
214     auto collection = cacheCollections_.find(cacheId);
215     return collection != cacheCollections_.cend() ? collection->second.get() : nullptr;
216 }
217 
RemoveFromCache(string_view uri,string_view contextUri)218 void EcsAssetManager::RemoveFromCache(string_view uri, string_view contextUri)
219 {
220     const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
221     const auto& cacheId = resolvedUri;
222 
223     cacheCollections_.erase(cacheId);
224 }
225 
ClearCache()226 void EcsAssetManager::ClearCache()
227 {
228     // NOTE: not removing any active collections.
229     for (auto it = cacheCollections_.begin(); it != cacheCollections_.end();) {
230         if (!it->second->IsActive()) {
231             it = cacheCollections_.erase(it);
232         } else {
233             ++it;
234         }
235     }
236 }
237 
RefreshAsset(IEntityCollection & ec,bool active)238 void EcsAssetManager::RefreshAsset(IEntityCollection& ec, bool active)
239 {
240     json::standalone_value json;
241     if (!GetEcsSerializer().WriteEntityCollection(ec, json)) {
242         return;
243     }
244 
245     if (json) {
246         ec.Clear();
247         // Set the active state when the collection empty to not iterate all the contents.
248         ec.SetActive(active);
249         // NOTE: This does conversion from standalone json to read only json.
250         GetEcsSerializer().ReadEntityCollection(ec, json, ec.GetContextUri());
251     }
252 }
253 
InstantiateCollection(IEntityCollection & ec,string_view uri,string_view contextUri)254 IEntityCollection* EcsAssetManager::InstantiateCollection(
255     IEntityCollection& ec, string_view uri, string_view contextUri)
256 {
257 #ifdef VERBOSE_LOGGING
258     CORE_LOG_D("InstantiateCollection: uri='%s' context='%s'", string(uri).c_str(), string(contextUri).c_str());
259 #endif
260     auto& ecs = ec.GetEcs();
261     auto externalCollection = LoadAssetToCache(ecs, uri, uri, contextUri, false, false);
262     if (externalCollection) {
263         auto& newCollection = ec.AddSubCollectionClone(*externalCollection, {});
264         newCollection.SetSrc(uri);
265         newCollection.MarkModified(false);
266         return &newCollection;
267     }
268 
269     CORE_LOG_E("Loading collection failed: '%s'", string(uri).c_str());
270     return nullptr;
271 }
272 
GetEcsSerializer()273 IEcsSerializer& EcsAssetManager::GetEcsSerializer()
274 {
275     return *ecsSerializer_;
276 }
277 
GetEcsSerializer() const278 const IEcsSerializer& EcsAssetManager::GetEcsSerializer() const
279 {
280     return *ecsSerializer_;
281 }
282 
GetExternalCollection(IEcs & ecs,string_view uri,string_view contextUri)283 IEntityCollection* EcsAssetManager::GetExternalCollection(IEcs& ecs, string_view uri, string_view contextUri)
284 {
285     // NOTE: Does not automatically try to load external dependencies.
286     return GetCachedCollection(uri, contextUri);
287 }
288 
Destroy()289 void EcsAssetManager::Destroy()
290 {
291     delete this;
292 }
293 
CreateEcsAssetManager(CORE3D_NS::IGraphicsContext & graphicsContext)294 IEcsAssetManager::Ptr CreateEcsAssetManager(CORE3D_NS::IGraphicsContext& graphicsContext)
295 {
296     return IEcsAssetManager::Ptr { new EcsAssetManager(graphicsContext) };
297 }
298 
299 ECS_SERIALIZER_END_NAMESPACE()
300