18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#include "asset_manager.h"
178bf80f4bSopenharmony_ci
188bf80f4bSopenharmony_ci#include <core/intf_engine.h>
198bf80f4bSopenharmony_ci#include <core/io/intf_file_manager.h>
208bf80f4bSopenharmony_ci#include <core/log.h>
218bf80f4bSopenharmony_ci#include <render/intf_render_context.h>
228bf80f4bSopenharmony_ci
238bf80f4bSopenharmony_ci#include "asset_loader.h"
248bf80f4bSopenharmony_ci#include "entity_collection.h"
258bf80f4bSopenharmony_ci#include "json.h"
268bf80f4bSopenharmony_ci
278bf80f4bSopenharmony_ciusing namespace BASE_NS;
288bf80f4bSopenharmony_ciusing namespace CORE_NS;
298bf80f4bSopenharmony_ciusing namespace RENDER_NS;
308bf80f4bSopenharmony_ciusing namespace CORE3D_NS;
318bf80f4bSopenharmony_ci
328bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
338bf80f4bSopenharmony_ci
348bf80f4bSopenharmony_ciAssetManager::AssetManager(IRenderContext& renderContext, IGraphicsContext& graphicsContext)
358bf80f4bSopenharmony_ci    : renderContext_(renderContext), graphicsContext_(graphicsContext), ecsSerializer_(renderContext_)
368bf80f4bSopenharmony_ci{
378bf80f4bSopenharmony_ci    ecsSerializer_.SetDefaultSerializers();
388bf80f4bSopenharmony_ci    ecsSerializer_.SetListener(this);
398bf80f4bSopenharmony_ci}
408bf80f4bSopenharmony_ci
418bf80f4bSopenharmony_ciAssetManager::~AssetManager() {}
428bf80f4bSopenharmony_ci
438bf80f4bSopenharmony_ciAssetManager::ExtensionType AssetManager::GetExtensionType(string_view ext) const
448bf80f4bSopenharmony_ci{
458bf80f4bSopenharmony_ci    // Ignore case.
468bf80f4bSopenharmony_ci    // Better type recognition
478bf80f4bSopenharmony_ci    if (ext == "collection") {
488bf80f4bSopenharmony_ci        return ExtensionType::COLLECTION;
498bf80f4bSopenharmony_ci    } else if (ext == "scene") {
508bf80f4bSopenharmony_ci        return ExtensionType::SCENE;
518bf80f4bSopenharmony_ci    } else if (ext == "animation") {
528bf80f4bSopenharmony_ci        return ExtensionType::ANIMATION;
538bf80f4bSopenharmony_ci    } else if (ext == "material") {
548bf80f4bSopenharmony_ci        return ExtensionType::MATERIAL;
558bf80f4bSopenharmony_ci    } else if (ext == "gltf") {
568bf80f4bSopenharmony_ci        return ExtensionType::GLTF;
578bf80f4bSopenharmony_ci    } else if (ext == "glb") {
588bf80f4bSopenharmony_ci        return ExtensionType::GLB;
598bf80f4bSopenharmony_ci    } else if (ext == "png") {
608bf80f4bSopenharmony_ci        return ExtensionType::PNG;
618bf80f4bSopenharmony_ci    } else if (ext == "jpg" || ext == "jpeg") {
628bf80f4bSopenharmony_ci        return ExtensionType::JPG;
638bf80f4bSopenharmony_ci    } else if (ext == "ktx") {
648bf80f4bSopenharmony_ci        return ExtensionType::KTX;
658bf80f4bSopenharmony_ci    } else {
668bf80f4bSopenharmony_ci        return ExtensionType::NOT_SUPPORTED;
678bf80f4bSopenharmony_ci    }
688bf80f4bSopenharmony_ci}
698bf80f4bSopenharmony_ci
708bf80f4bSopenharmony_ciIAssetLoader::Ptr AssetManager::CreateAssetLoader(IEntityCollection& ec, string_view src, string_view contextUri)
718bf80f4bSopenharmony_ci{
728bf80f4bSopenharmony_ci    return SCENE_NS::CreateAssetLoader(*this, renderContext_, graphicsContext_, ec, src, contextUri);
738bf80f4bSopenharmony_ci}
748bf80f4bSopenharmony_ci
758bf80f4bSopenharmony_cibool AssetManager::LoadAsset(IEntityCollection& ec, string_view uri, string_view contextUri)
768bf80f4bSopenharmony_ci{
778bf80f4bSopenharmony_ci    auto assetLoader = CreateAssetLoader(ec, uri, contextUri);
788bf80f4bSopenharmony_ci    if (assetLoader) {
798bf80f4bSopenharmony_ci        //  Use syncronous asset loading.
808bf80f4bSopenharmony_ci        assetLoader->LoadAsset();
818bf80f4bSopenharmony_ci        auto result = assetLoader->GetResult();
828bf80f4bSopenharmony_ci        return result.success;
838bf80f4bSopenharmony_ci    }
848bf80f4bSopenharmony_ci    return false;
858bf80f4bSopenharmony_ci}
868bf80f4bSopenharmony_ci
878bf80f4bSopenharmony_cibool SaveTextFile(string_view fileUri, string_view fileContents, CORE_NS::IFileManager& fileManager)
888bf80f4bSopenharmony_ci{
898bf80f4bSopenharmony_ci    // TODO: the safest way to save files would be to save to a temp file and rename.
908bf80f4bSopenharmony_ci    SCENE_PLUGIN_VERBOSE_LOG("Save file: '%s'", string(fileUri).c_str());
918bf80f4bSopenharmony_ci    auto file = fileManager.CreateFile(fileUri);
928bf80f4bSopenharmony_ci    if (file) {
938bf80f4bSopenharmony_ci        file->Write(fileContents.data(), fileContents.length());
948bf80f4bSopenharmony_ci        file->Close();
958bf80f4bSopenharmony_ci        return true;
968bf80f4bSopenharmony_ci    }
978bf80f4bSopenharmony_ci
988bf80f4bSopenharmony_ci    return false;
998bf80f4bSopenharmony_ci}
1008bf80f4bSopenharmony_ci
1018bf80f4bSopenharmony_cibool AssetManager::SaveJsonEntityCollection(const IEntityCollection& ec, string_view uri, string_view contextUri) const
1028bf80f4bSopenharmony_ci{
1038bf80f4bSopenharmony_ci    const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
1048bf80f4bSopenharmony_ci    SCENE_PLUGIN_VERBOSE_LOG("SaveJsonEntityCollection: '%s'", resolvedUri.c_str());
1058bf80f4bSopenharmony_ci
1068bf80f4bSopenharmony_ci    json::standalone_value jsonOut;
1078bf80f4bSopenharmony_ci    auto result = ecsSerializer_.WriteEntityCollection(ec, jsonOut);
1088bf80f4bSopenharmony_ci    if (result) {
1098bf80f4bSopenharmony_ci        const string jsonString = CORE_NS::to_formatted_string(jsonOut, 4);
1108bf80f4bSopenharmony_ci        if (SaveTextFile(
1118bf80f4bSopenharmony_ci                resolvedUri, { jsonString.data(), jsonString.size() }, renderContext_.GetEngine().GetFileManager())) {
1128bf80f4bSopenharmony_ci            return true;
1138bf80f4bSopenharmony_ci        }
1148bf80f4bSopenharmony_ci    }
1158bf80f4bSopenharmony_ci    return false;
1168bf80f4bSopenharmony_ci}
1178bf80f4bSopenharmony_ci
1188bf80f4bSopenharmony_ciIEntityCollection* AssetManager::LoadAssetToCache(
1198bf80f4bSopenharmony_ci    IEcs& ecs, string_view cacheUri, string_view uri, string_view contextUri, bool active, bool forceReload)
1208bf80f4bSopenharmony_ci{
1218bf80f4bSopenharmony_ci    // Check if this collection was already loaded.
1228bf80f4bSopenharmony_ci    const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
1238bf80f4bSopenharmony_ci    const auto& cacheId = cacheUri;
1248bf80f4bSopenharmony_ci
1258bf80f4bSopenharmony_ci    if (!forceReload) {
1268bf80f4bSopenharmony_ci        auto it = cacheCollections_.find(cacheId);
1278bf80f4bSopenharmony_ci        if (it != cacheCollections_.cend()) {
1288bf80f4bSopenharmony_ci            return it->second.get();
1298bf80f4bSopenharmony_ci        }
1308bf80f4bSopenharmony_ci    }
1318bf80f4bSopenharmony_ci
1328bf80f4bSopenharmony_ci    // Need to first remove the possible cached collection because otherwise gltf importer will not reload the resources
1338bf80f4bSopenharmony_ci    // like meshes if they have the same names.
1348bf80f4bSopenharmony_ci    cacheCollections_.erase(cacheId);
1358bf80f4bSopenharmony_ci
1368bf80f4bSopenharmony_ci    // Load the entity collection.
1378bf80f4bSopenharmony_ci
1388bf80f4bSopenharmony_ci    auto ec = EntityCollection::Ptr { new EntityCollection(ecs, uri, contextUri) };
1398bf80f4bSopenharmony_ci    if (LoadAsset(*ec, uri, contextUri)) {
1408bf80f4bSopenharmony_ci        // Something was loaded. Set all loaded entities as inactive if requested.
1418bf80f4bSopenharmony_ci        if (!active) {
1428bf80f4bSopenharmony_ci            ec->SetActive(false);
1438bf80f4bSopenharmony_ci        }
1448bf80f4bSopenharmony_ci        // Add loaded collection to cache map.
1458bf80f4bSopenharmony_ci        return (cacheCollections_[cacheId] = move(ec)).get();
1468bf80f4bSopenharmony_ci    } else {
1478bf80f4bSopenharmony_ci        return nullptr;
1488bf80f4bSopenharmony_ci    }
1498bf80f4bSopenharmony_ci}
1508bf80f4bSopenharmony_ci
1518bf80f4bSopenharmony_cibool AssetManager::IsCachedCollection(string_view uri, string_view contextUri) const
1528bf80f4bSopenharmony_ci{
1538bf80f4bSopenharmony_ci    const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
1548bf80f4bSopenharmony_ci    const auto& cacheId = resolvedUri;
1558bf80f4bSopenharmony_ci
1568bf80f4bSopenharmony_ci    return cacheCollections_.find(cacheId) != cacheCollections_.cend();
1578bf80f4bSopenharmony_ci}
1588bf80f4bSopenharmony_ci
1598bf80f4bSopenharmony_ciIEntityCollection* AssetManager::CreateCachedCollection(
1608bf80f4bSopenharmony_ci    IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri)
1618bf80f4bSopenharmony_ci{
1628bf80f4bSopenharmony_ci    auto ec = EntityCollection::Ptr { new EntityCollection(ecs, uri, contextUri) };
1638bf80f4bSopenharmony_ci    ec->SetActive(false);
1648bf80f4bSopenharmony_ci
1658bf80f4bSopenharmony_ci    const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
1668bf80f4bSopenharmony_ci    const auto& cacheId = resolvedUri;
1678bf80f4bSopenharmony_ci
1688bf80f4bSopenharmony_ci    // Add the created collection to the cache map.
1698bf80f4bSopenharmony_ci    return (cacheCollections_[cacheId] = move(ec)).get();
1708bf80f4bSopenharmony_ci}
1718bf80f4bSopenharmony_ci
1728bf80f4bSopenharmony_ciIEntityCollection* AssetManager::GetCachedCollection(string_view uri, string_view contextUri) const
1738bf80f4bSopenharmony_ci{
1748bf80f4bSopenharmony_ci    const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
1758bf80f4bSopenharmony_ci    const auto& cacheId = resolvedUri;
1768bf80f4bSopenharmony_ci
1778bf80f4bSopenharmony_ci    auto collection = cacheCollections_.find(cacheId);
1788bf80f4bSopenharmony_ci    return collection != cacheCollections_.cend() ? collection->second.get() : nullptr;
1798bf80f4bSopenharmony_ci}
1808bf80f4bSopenharmony_ci
1818bf80f4bSopenharmony_civoid AssetManager::RemoveFromCache(string_view uri, string_view contextUri)
1828bf80f4bSopenharmony_ci{
1838bf80f4bSopenharmony_ci    const auto resolvedUri = PathUtil::ResolveUri(contextUri, uri);
1848bf80f4bSopenharmony_ci    const auto& cacheId = resolvedUri;
1858bf80f4bSopenharmony_ci
1868bf80f4bSopenharmony_ci    cacheCollections_.erase(cacheId);
1878bf80f4bSopenharmony_ci}
1888bf80f4bSopenharmony_ci
1898bf80f4bSopenharmony_civoid AssetManager::ClearCache()
1908bf80f4bSopenharmony_ci{
1918bf80f4bSopenharmony_ci    // NOTE: not removing any active collections.
1928bf80f4bSopenharmony_ci    for (auto it = cacheCollections_.begin(); it != cacheCollections_.end();) {
1938bf80f4bSopenharmony_ci        if (!it->second->IsActive()) {
1948bf80f4bSopenharmony_ci            it = cacheCollections_.erase(it);
1958bf80f4bSopenharmony_ci        } else {
1968bf80f4bSopenharmony_ci            ++it;
1978bf80f4bSopenharmony_ci        }
1988bf80f4bSopenharmony_ci    }
1998bf80f4bSopenharmony_ci}
2008bf80f4bSopenharmony_ci
2018bf80f4bSopenharmony_civoid AssetManager::RefreshAsset(IEntityCollection& ec, bool active)
2028bf80f4bSopenharmony_ci{
2038bf80f4bSopenharmony_ci    json::standalone_value json;
2048bf80f4bSopenharmony_ci    if (!ecsSerializer_.WriteEntityCollection(ec, json)) {
2058bf80f4bSopenharmony_ci        return;
2068bf80f4bSopenharmony_ci    }
2078bf80f4bSopenharmony_ci
2088bf80f4bSopenharmony_ci    if (json) {
2098bf80f4bSopenharmony_ci        ec.Clear();
2108bf80f4bSopenharmony_ci        // Set the active state when the collection empty to not iterate all the contents.
2118bf80f4bSopenharmony_ci        ec.SetActive(active);
2128bf80f4bSopenharmony_ci        // NOTE: This does conversion from standalone json to read only json.
2138bf80f4bSopenharmony_ci        ecsSerializer_.ReadEntityCollection(ec, json, ec.GetContextUri());
2148bf80f4bSopenharmony_ci    }
2158bf80f4bSopenharmony_ci}
2168bf80f4bSopenharmony_ci
2178bf80f4bSopenharmony_ciIEntityCollection* AssetManager::InstantiateCollection(IEntityCollection& ec, string_view uri, string_view contextUri)
2188bf80f4bSopenharmony_ci{
2198bf80f4bSopenharmony_ci#ifdef VERBOSE_LOGGING
2208bf80f4bSopenharmony_ci    SCENE_PLUGIN_VERBOSE_LOG(
2218bf80f4bSopenharmony_ci        "InstantiateCollection: uri='%s' context='%s'", string(uri).c_str(), string(contextUri).c_str());
2228bf80f4bSopenharmony_ci#endif
2238bf80f4bSopenharmony_ci    auto& ecs = ec.GetEcs();
2248bf80f4bSopenharmony_ci    auto externalCollection = LoadAssetToCache(ecs, uri, uri, contextUri, false, false);
2258bf80f4bSopenharmony_ci    if (externalCollection) {
2268bf80f4bSopenharmony_ci        auto& newCollection = ec.AddSubCollectionClone(*externalCollection, {});
2278bf80f4bSopenharmony_ci        newCollection.SetSrc(uri);
2288bf80f4bSopenharmony_ci        newCollection.MarkModified(false);
2298bf80f4bSopenharmony_ci        return &newCollection;
2308bf80f4bSopenharmony_ci    }
2318bf80f4bSopenharmony_ci
2328bf80f4bSopenharmony_ci    CORE_LOG_E("Loading collection failed: '%s'", string(uri).c_str());
2338bf80f4bSopenharmony_ci    return nullptr;
2348bf80f4bSopenharmony_ci}
2358bf80f4bSopenharmony_ci
2368bf80f4bSopenharmony_ciIEcsSerializer& AssetManager::GetEcsSerializer()
2378bf80f4bSopenharmony_ci{
2388bf80f4bSopenharmony_ci    return ecsSerializer_;
2398bf80f4bSopenharmony_ci}
2408bf80f4bSopenharmony_ci
2418bf80f4bSopenharmony_ciIEntityCollection* AssetManager::GetExternalCollection(IEcs& ecs, string_view uri, string_view contextUri)
2428bf80f4bSopenharmony_ci{
2438bf80f4bSopenharmony_ci    // NOTE: Does not automatically try to load external dependencies.
2448bf80f4bSopenharmony_ci    return GetCachedCollection(uri, contextUri);
2458bf80f4bSopenharmony_ci}
2468bf80f4bSopenharmony_ci
2478bf80f4bSopenharmony_civoid AssetManager::Destroy()
2488bf80f4bSopenharmony_ci{
2498bf80f4bSopenharmony_ci    delete this;
2508bf80f4bSopenharmony_ci}
2518bf80f4bSopenharmony_ci
2528bf80f4bSopenharmony_ciSCENE_END_NAMESPACE()
253