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 "asset_manager.h"
17
18 #include <core/intf_engine.h>
19 #include <core/io/intf_file_manager.h>
20 #include <core/log.h>
21 #include <render/intf_render_context.h>
22
23 #include "asset_loader.h"
24 #include "entity_collection.h"
25 #include "json.h"
26
27 using namespace BASE_NS;
28 using namespace CORE_NS;
29 using namespace RENDER_NS;
30 using namespace CORE3D_NS;
31
SCENE_BEGIN_NAMESPACE()32 SCENE_BEGIN_NAMESPACE()
33
34 AssetManager::AssetManager(IRenderContext& renderContext, IGraphicsContext& graphicsContext)
35 : renderContext_(renderContext), graphicsContext_(graphicsContext), ecsSerializer_(renderContext_)
36 {
37 ecsSerializer_.SetDefaultSerializers();
38 ecsSerializer_.SetListener(this);
39 }
40
~AssetManager()41 AssetManager::~AssetManager() {}
42
GetExtensionType(string_view ext) const43 AssetManager::ExtensionType AssetManager::GetExtensionType(string_view ext) const
44 {
45 // Ignore case.
46 // Better type recognition
47 if (ext == "collection") {
48 return ExtensionType::COLLECTION;
49 } else if (ext == "scene") {
50 return ExtensionType::SCENE;
51 } else if (ext == "animation") {
52 return ExtensionType::ANIMATION;
53 } else if (ext == "material") {
54 return ExtensionType::MATERIAL;
55 } else if (ext == "gltf") {
56 return ExtensionType::GLTF;
57 } else if (ext == "glb") {
58 return ExtensionType::GLB;
59 } else if (ext == "png") {
60 return ExtensionType::PNG;
61 } else if (ext == "jpg" || ext == "jpeg") {
62 return ExtensionType::JPG;
63 } else if (ext == "ktx") {
64 return ExtensionType::KTX;
65 } else {
66 return ExtensionType::NOT_SUPPORTED;
67 }
68 }
69
CreateAssetLoader(IEntityCollection & ec,string_view src,string_view contextUri)70 IAssetLoader::Ptr AssetManager::CreateAssetLoader(IEntityCollection& ec, string_view src, string_view contextUri)
71 {
72 return SCENE_NS::CreateAssetLoader(*this, renderContext_, graphicsContext_, ec, src, contextUri);
73 }
74
LoadAsset(IEntityCollection & ec,string_view uri,string_view contextUri)75 bool AssetManager::LoadAsset(IEntityCollection& ec, string_view uri, string_view contextUri)
76 {
77 auto assetLoader = CreateAssetLoader(ec, uri, contextUri);
78 if (assetLoader) {
79 // Use syncronous asset loading.
80 assetLoader->LoadAsset();
81 auto result = assetLoader->GetResult();
82 return result.success;
83 }
84 return false;
85 }
86
SaveTextFile(string_view fileUri,string_view fileContents,CORE_NS::IFileManager & fileManager)87 bool SaveTextFile(string_view fileUri, string_view fileContents, CORE_NS::IFileManager& fileManager)
88 {
89 // TODO: the safest way to save files would be to save to a temp file and rename.
90 SCENE_PLUGIN_VERBOSE_LOG("Save file: '%s'", string(fileUri).c_str());
91 auto file = fileManager.CreateFile(fileUri);
92 if (file) {
93 file->Write(fileContents.data(), fileContents.length());
94 file->Close();
95 return true;
96 }
97
98 return false;
99 }
100
SaveJsonEntityCollection(const IEntityCollection & ec,string_view uri,string_view contextUri) const101 bool AssetManager::SaveJsonEntityCollection(const IEntityCollection& ec, string_view uri, string_view contextUri) const
102 {
103 const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
104 SCENE_PLUGIN_VERBOSE_LOG("SaveJsonEntityCollection: '%s'", resolvedUri.c_str());
105
106 json::standalone_value jsonOut;
107 auto result = ecsSerializer_.WriteEntityCollection(ec, jsonOut);
108 if (result) {
109 const string jsonString = CORE_NS::to_formatted_string(jsonOut, 4);
110 if (SaveTextFile(
111 resolvedUri, { jsonString.data(), jsonString.size() }, renderContext_.GetEngine().GetFileManager())) {
112 return true;
113 }
114 }
115 return false;
116 }
117
LoadAssetToCache(IEcs & ecs,string_view cacheUri,string_view uri,string_view contextUri,bool active,bool forceReload)118 IEntityCollection* AssetManager::LoadAssetToCache(
119 IEcs& ecs, string_view cacheUri, string_view uri, string_view contextUri, bool active, bool forceReload)
120 {
121 // Check if this collection was already loaded.
122 const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
123 const auto& cacheId = cacheUri;
124
125 if (!forceReload) {
126 auto it = cacheCollections_.find(cacheId);
127 if (it != cacheCollections_.cend()) {
128 return it->second.get();
129 }
130 }
131
132 // Need to first remove the possible cached collection because otherwise gltf importer will not reload the resources
133 // like meshes if they have the same names.
134 cacheCollections_.erase(cacheId);
135
136 // Load the entity collection.
137
138 auto ec = EntityCollection::Ptr { new EntityCollection(ecs, uri, contextUri) };
139 if (LoadAsset(*ec, uri, contextUri)) {
140 // Something was loaded. Set all loaded entities as inactive if requested.
141 if (!active) {
142 ec->SetActive(false);
143 }
144 // Add loaded collection to cache map.
145 return (cacheCollections_[cacheId] = move(ec)).get();
146 } else {
147 return nullptr;
148 }
149 }
150
IsCachedCollection(string_view uri,string_view contextUri) const151 bool AssetManager::IsCachedCollection(string_view uri, string_view contextUri) const
152 {
153 const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
154 const auto& cacheId = resolvedUri;
155
156 return cacheCollections_.find(cacheId) != cacheCollections_.cend();
157 }
158
CreateCachedCollection(IEcs & ecs,BASE_NS::string_view uri,BASE_NS::string_view contextUri)159 IEntityCollection* AssetManager::CreateCachedCollection(
160 IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri)
161 {
162 auto ec = EntityCollection::Ptr { new EntityCollection(ecs, uri, contextUri) };
163 ec->SetActive(false);
164
165 const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
166 const auto& cacheId = resolvedUri;
167
168 // Add the created collection to the cache map.
169 return (cacheCollections_[cacheId] = move(ec)).get();
170 }
171
GetCachedCollection(string_view uri,string_view contextUri) const172 IEntityCollection* AssetManager::GetCachedCollection(string_view uri, string_view contextUri) const
173 {
174 const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
175 const auto& cacheId = resolvedUri;
176
177 auto collection = cacheCollections_.find(cacheId);
178 return collection != cacheCollections_.cend() ? collection->second.get() : nullptr;
179 }
180
RemoveFromCache(string_view uri,string_view contextUri)181 void AssetManager::RemoveFromCache(string_view uri, string_view contextUri)
182 {
183 const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
184 const auto& cacheId = resolvedUri;
185
186 cacheCollections_.erase(cacheId);
187 }
188
ClearCache()189 void AssetManager::ClearCache()
190 {
191 // NOTE: not removing any active collections.
192 for (auto it = cacheCollections_.begin(); it != cacheCollections_.end();) {
193 if (!it->second->IsActive()) {
194 it = cacheCollections_.erase(it);
195 } else {
196 ++it;
197 }
198 }
199 }
200
RefreshAsset(IEntityCollection & ec,bool active)201 void AssetManager::RefreshAsset(IEntityCollection& ec, bool active)
202 {
203 json::standalone_value json;
204 if (!ecsSerializer_.WriteEntityCollection(ec, json)) {
205 return;
206 }
207
208 if (json) {
209 ec.Clear();
210 // Set the active state when the collection empty to not iterate all the contents.
211 ec.SetActive(active);
212 // NOTE: This does conversion from standalone json to read only json.
213 ecsSerializer_.ReadEntityCollection(ec, json, ec.GetContextUri());
214 }
215 }
216
InstantiateCollection(IEntityCollection & ec,string_view uri,string_view contextUri)217 IEntityCollection* AssetManager::InstantiateCollection(IEntityCollection& ec, string_view uri, string_view contextUri)
218 {
219 #ifdef VERBOSE_LOGGING
220 SCENE_PLUGIN_VERBOSE_LOG(
221 "InstantiateCollection: uri='%s' context='%s'", string(uri).c_str(), string(contextUri).c_str());
222 #endif
223 auto& ecs = ec.GetEcs();
224 auto externalCollection = LoadAssetToCache(ecs, uri, uri, contextUri, false, false);
225 if (externalCollection) {
226 auto& newCollection = ec.AddSubCollectionClone(*externalCollection, {});
227 newCollection.SetSrc(uri);
228 newCollection.MarkModified(false);
229 return &newCollection;
230 }
231
232 CORE_LOG_E("Loading collection failed: '%s'", string(uri).c_str());
233 return nullptr;
234 }
235
GetEcsSerializer()236 IEcsSerializer& AssetManager::GetEcsSerializer()
237 {
238 return ecsSerializer_;
239 }
240
GetExternalCollection(IEcs & ecs,string_view uri,string_view contextUri)241 IEntityCollection* AssetManager::GetExternalCollection(IEcs& ecs, string_view uri, string_view contextUri)
242 {
243 // NOTE: Does not automatically try to load external dependencies.
244 return GetCachedCollection(uri, contextUri);
245 }
246
Destroy()247 void AssetManager::Destroy()
248 {
249 delete this;
250 }
251
252 SCENE_END_NAMESPACE()
253