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_loader.h"
178bf80f4bSopenharmony_ci
188bf80f4bSopenharmony_ci#include <algorithm>
198bf80f4bSopenharmony_ci
208bf80f4bSopenharmony_ci#include <3d/ecs/components/animation_component.h>
218bf80f4bSopenharmony_ci#include <3d/ecs/components/animation_track_component.h>
228bf80f4bSopenharmony_ci#include <3d/ecs/components/name_component.h>
238bf80f4bSopenharmony_ci#include <3d/ecs/components/node_component.h>
248bf80f4bSopenharmony_ci#include <3d/ecs/components/render_handle_component.h>
258bf80f4bSopenharmony_ci#include <3d/ecs/components/uri_component.h>
268bf80f4bSopenharmony_ci#include <3d/ecs/systems/intf_animation_system.h>
278bf80f4bSopenharmony_ci#include <3d/ecs/systems/intf_node_system.h>
288bf80f4bSopenharmony_ci#include <base/containers/fixed_string.h>
298bf80f4bSopenharmony_ci#include <base/containers/vector.h>
308bf80f4bSopenharmony_ci#include <core/image/intf_image_loader_manager.h>
318bf80f4bSopenharmony_ci#include <core/intf_engine.h>
328bf80f4bSopenharmony_ci#include <core/io/intf_file_manager.h>
338bf80f4bSopenharmony_ci#include <render/device/intf_gpu_resource_manager.h>
348bf80f4bSopenharmony_ci#include <render/intf_render_context.h>
358bf80f4bSopenharmony_ci
368bf80f4bSopenharmony_ci#include "asset_manager.h"
378bf80f4bSopenharmony_ci#include "asset_migration.h"
388bf80f4bSopenharmony_ci
398bf80f4bSopenharmony_ciusing namespace BASE_NS;
408bf80f4bSopenharmony_ciusing namespace CORE_NS;
418bf80f4bSopenharmony_ciusing namespace RENDER_NS;
428bf80f4bSopenharmony_ciusing namespace CORE3D_NS;
438bf80f4bSopenharmony_ci
448bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
458bf80f4bSopenharmony_ci
468bf80f4bSopenharmony_cinamespace {
478bf80f4bSopenharmony_ci
488bf80f4bSopenharmony_cistruct IoUtil {
498bf80f4bSopenharmony_ci    static bool LoadTextFile(string_view fileUri, string& fileContentsOut, IFileManager& fileManager)
508bf80f4bSopenharmony_ci    {
518bf80f4bSopenharmony_ci        auto file = fileManager.OpenFile(fileUri);
528bf80f4bSopenharmony_ci        if (file) {
538bf80f4bSopenharmony_ci            const size_t length = file->GetLength();
548bf80f4bSopenharmony_ci            fileContentsOut.resize(length);
558bf80f4bSopenharmony_ci            return file->Read(fileContentsOut.data(), length) == length;
568bf80f4bSopenharmony_ci        }
578bf80f4bSopenharmony_ci        return false;
588bf80f4bSopenharmony_ci    }
598bf80f4bSopenharmony_ci};
608bf80f4bSopenharmony_ci
618bf80f4bSopenharmony_ci// Add a node and all of its children recursively to a collection
628bf80f4bSopenharmony_civoid AddNodeToCollectionRecursive(IEntityCollection& ec, ISceneNode& node, string_view path)
638bf80f4bSopenharmony_ci{
648bf80f4bSopenharmony_ci    const auto entity = node.GetEntity();
658bf80f4bSopenharmony_ci    const auto currentPath = path + node.GetName();
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_ci    EntityReference ref = ec.GetEcs().GetEntityManager().GetReferenceCounted(entity);
688bf80f4bSopenharmony_ci    ec.AddEntity(ref);
698bf80f4bSopenharmony_ci    ec.SetId(currentPath, ref);
708bf80f4bSopenharmony_ci
718bf80f4bSopenharmony_ci    const auto childBasePath = currentPath + "/";
728bf80f4bSopenharmony_ci    for (auto* child : node.GetChildren()) {
738bf80f4bSopenharmony_ci        AddNodeToCollectionRecursive(ec, *child, childBasePath);
748bf80f4bSopenharmony_ci    }
758bf80f4bSopenharmony_ci}
768bf80f4bSopenharmony_ci} // namespace
778bf80f4bSopenharmony_ci
788bf80f4bSopenharmony_ci// A class that executes asset load operation over multiple frames.
798bf80f4bSopenharmony_ciclass AssetLoader final : public IAssetLoader, private IAssetLoader::IListener, private IGLTF2Importer::Listener {
808bf80f4bSopenharmony_cipublic:
818bf80f4bSopenharmony_ci    AssetLoader(AssetManager& assetManager, IRenderContext& renderContext, IGraphicsContext& graphicsContext,
828bf80f4bSopenharmony_ci        IEntityCollection& ec, string_view src, string_view contextUri)
838bf80f4bSopenharmony_ci        : assetManager_(assetManager), renderContext_(renderContext), graphicsContext_(graphicsContext),
848bf80f4bSopenharmony_ci          ecs_(ec.GetEcs()), ec_(ec), src_(src), contextUri_(contextUri),
858bf80f4bSopenharmony_ci          fileManager_(renderContext_.GetEngine().GetFileManager())
868bf80f4bSopenharmony_ci    {}
878bf80f4bSopenharmony_ci
888bf80f4bSopenharmony_ci    ~AssetLoader() override
898bf80f4bSopenharmony_ci    {
908bf80f4bSopenharmony_ci        Cancel();
918bf80f4bSopenharmony_ci    }
928bf80f4bSopenharmony_ci
938bf80f4bSopenharmony_ci    IEntityCollection& GetEntityCollection() const override
948bf80f4bSopenharmony_ci    {
958bf80f4bSopenharmony_ci        return ec_;
968bf80f4bSopenharmony_ci    }
978bf80f4bSopenharmony_ci
988bf80f4bSopenharmony_ci    string GetSrc() const override
998bf80f4bSopenharmony_ci    {
1008bf80f4bSopenharmony_ci        return src_;
1018bf80f4bSopenharmony_ci    }
1028bf80f4bSopenharmony_ci
1038bf80f4bSopenharmony_ci    string GetContextUri() const override
1048bf80f4bSopenharmony_ci    {
1058bf80f4bSopenharmony_ci        return contextUri_;
1068bf80f4bSopenharmony_ci    }
1078bf80f4bSopenharmony_ci
1088bf80f4bSopenharmony_ci    string GetUri() const override
1098bf80f4bSopenharmony_ci    {
1108bf80f4bSopenharmony_ci        return PathUtil::ResolveUri(contextUri_, src_, true);
1118bf80f4bSopenharmony_ci    }
1128bf80f4bSopenharmony_ci
1138bf80f4bSopenharmony_ci    void AddListener(IAssetLoader::IListener& listener) override
1148bf80f4bSopenharmony_ci    {
1158bf80f4bSopenharmony_ci        CORE_ASSERT(&listener);
1168bf80f4bSopenharmony_ci        listeners_.emplace_back(&listener);
1178bf80f4bSopenharmony_ci    }
1188bf80f4bSopenharmony_ci
1198bf80f4bSopenharmony_ci    void RemoveListener(IAssetLoader::IListener& listener) override
1208bf80f4bSopenharmony_ci    {
1218bf80f4bSopenharmony_ci        CORE_ASSERT(&listener);
1228bf80f4bSopenharmony_ci        for (size_t i = 0; i < listeners_.size(); ++i) {
1238bf80f4bSopenharmony_ci            if (&listener == listeners_[i]) {
1248bf80f4bSopenharmony_ci                listeners_.erase(listeners_.begin() + i);
1258bf80f4bSopenharmony_ci                return;
1268bf80f4bSopenharmony_ci            }
1278bf80f4bSopenharmony_ci        }
1288bf80f4bSopenharmony_ci
1298bf80f4bSopenharmony_ci        // trying to remove a non-existent listener.
1308bf80f4bSopenharmony_ci        CORE_ASSERT(true);
1318bf80f4bSopenharmony_ci    }
1328bf80f4bSopenharmony_ci
1338bf80f4bSopenharmony_ci    void LoadAsset() override
1348bf80f4bSopenharmony_ci    {
1358bf80f4bSopenharmony_ci        async_ = false;
1368bf80f4bSopenharmony_ci        StartLoading();
1378bf80f4bSopenharmony_ci    }
1388bf80f4bSopenharmony_ci
1398bf80f4bSopenharmony_ci    void LoadAssetAsync() override
1408bf80f4bSopenharmony_ci    {
1418bf80f4bSopenharmony_ci        async_ = true;
1428bf80f4bSopenharmony_ci        StartLoading();
1438bf80f4bSopenharmony_ci    }
1448bf80f4bSopenharmony_ci
1458bf80f4bSopenharmony_ci    IAssetLoader* GetNextDependency() const
1468bf80f4bSopenharmony_ci    {
1478bf80f4bSopenharmony_ci        if (dependencies_.empty()) {
1488bf80f4bSopenharmony_ci            return nullptr;
1498bf80f4bSopenharmony_ci        }
1508bf80f4bSopenharmony_ci        for (const auto& dep : dependencies_) {
1518bf80f4bSopenharmony_ci            if (dep && !dep->IsCompleted()) {
1528bf80f4bSopenharmony_ci                return dep.get();
1538bf80f4bSopenharmony_ci            }
1548bf80f4bSopenharmony_ci        }
1558bf80f4bSopenharmony_ci        return nullptr;
1568bf80f4bSopenharmony_ci    }
1578bf80f4bSopenharmony_ci
1588bf80f4bSopenharmony_ci    //
1598bf80f4bSopenharmony_ci    // From IAssetLoader::Listener
1608bf80f4bSopenharmony_ci    //
1618bf80f4bSopenharmony_ci    void OnLoadFinished(const IAssetLoader& loader) override
1628bf80f4bSopenharmony_ci    {
1638bf80f4bSopenharmony_ci        // This will be called when a dependency has finished loading.
1648bf80f4bSopenharmony_ci
1658bf80f4bSopenharmony_ci        // Hide cached data.
1668bf80f4bSopenharmony_ci        loader.GetEntityCollection().SetActive(false);
1678bf80f4bSopenharmony_ci
1688bf80f4bSopenharmony_ci        auto* dep = GetNextDependency();
1698bf80f4bSopenharmony_ci        if (dep) {
1708bf80f4bSopenharmony_ci            // Load next dependency.
1718bf80f4bSopenharmony_ci            ContinueLoading();
1728bf80f4bSopenharmony_ci        } else {
1738bf80f4bSopenharmony_ci            // There are no more dependencies. Try loading the main asset again but now with the dependencies loaded.
1748bf80f4bSopenharmony_ci            StartLoading();
1758bf80f4bSopenharmony_ci        }
1768bf80f4bSopenharmony_ci    }
1778bf80f4bSopenharmony_ci
1788bf80f4bSopenharmony_ci    //
1798bf80f4bSopenharmony_ci    // From CORE_NS::IGLTF2Importer::Listener
1808bf80f4bSopenharmony_ci    //
1818bf80f4bSopenharmony_ci    void OnImportStarted() override {}
1828bf80f4bSopenharmony_ci    void OnImportFinished() override
1838bf80f4bSopenharmony_ci    {
1848bf80f4bSopenharmony_ci        GltfImportFinished();
1858bf80f4bSopenharmony_ci        if (async_) {
1868bf80f4bSopenharmony_ci            ContinueLoading();
1878bf80f4bSopenharmony_ci        }
1888bf80f4bSopenharmony_ci    }
1898bf80f4bSopenharmony_ci    void OnImportProgressed(size_t taskIndex, size_t taskCount) override {}
1908bf80f4bSopenharmony_ci
1918bf80f4bSopenharmony_ci    bool Execute(uint32_t timeBudget) override
1928bf80f4bSopenharmony_ci    {
1938bf80f4bSopenharmony_ci        // NOTE: Currently actually only one import will be active at a time so we don't need to worry about the time
1948bf80f4bSopenharmony_ci        // budget.
1958bf80f4bSopenharmony_ci        bool done = true;
1968bf80f4bSopenharmony_ci
1978bf80f4bSopenharmony_ci        for (auto& dep : dependencies_) {
1988bf80f4bSopenharmony_ci            if (dep) {
1998bf80f4bSopenharmony_ci                done &= dep->Execute(timeBudget);
2008bf80f4bSopenharmony_ci            }
2018bf80f4bSopenharmony_ci        }
2028bf80f4bSopenharmony_ci        if (importer_) {
2038bf80f4bSopenharmony_ci            done &= importer_->Execute(timeBudget);
2048bf80f4bSopenharmony_ci        }
2058bf80f4bSopenharmony_ci        return done;
2068bf80f4bSopenharmony_ci    }
2078bf80f4bSopenharmony_ci
2088bf80f4bSopenharmony_ci    void Cancel() override
2098bf80f4bSopenharmony_ci    {
2108bf80f4bSopenharmony_ci        cancelled_ = true;
2118bf80f4bSopenharmony_ci        for (auto& dep : dependencies_) {
2128bf80f4bSopenharmony_ci            if (dep) {
2138bf80f4bSopenharmony_ci                dep->Cancel();
2148bf80f4bSopenharmony_ci            }
2158bf80f4bSopenharmony_ci        }
2168bf80f4bSopenharmony_ci
2178bf80f4bSopenharmony_ci        if (importer_) {
2188bf80f4bSopenharmony_ci            importer_->Cancel();
2198bf80f4bSopenharmony_ci            importer_.reset();
2208bf80f4bSopenharmony_ci        }
2218bf80f4bSopenharmony_ci    }
2228bf80f4bSopenharmony_ci
2238bf80f4bSopenharmony_ci    bool IsCompleted() const override
2248bf80f4bSopenharmony_ci    {
2258bf80f4bSopenharmony_ci        return done_;
2268bf80f4bSopenharmony_ci    }
2278bf80f4bSopenharmony_ci
2288bf80f4bSopenharmony_ci    Result GetResult() const override
2298bf80f4bSopenharmony_ci    {
2308bf80f4bSopenharmony_ci        return result_;
2318bf80f4bSopenharmony_ci    }
2328bf80f4bSopenharmony_ci
2338bf80f4bSopenharmony_ci    void Destroy() override
2348bf80f4bSopenharmony_ci    {
2358bf80f4bSopenharmony_ci        delete this;
2368bf80f4bSopenharmony_ci    }
2378bf80f4bSopenharmony_ci
2388bf80f4bSopenharmony_ciprivate:
2398bf80f4bSopenharmony_ci    void StartLoading()
2408bf80f4bSopenharmony_ci    {
2418bf80f4bSopenharmony_ci        if (src_.empty()) {
2428bf80f4bSopenharmony_ci            result_ = { false, "Ivalid uri" };
2438bf80f4bSopenharmony_ci            done_ = true;
2448bf80f4bSopenharmony_ci            return;
2458bf80f4bSopenharmony_ci        }
2468bf80f4bSopenharmony_ci
2478bf80f4bSopenharmony_ci        const auto resolvedUri = GetUri();
2488bf80f4bSopenharmony_ci
2498bf80f4bSopenharmony_ci        const auto resolvedFile = PathUtil::ResolveUri(contextUri_, src_, false);
2508bf80f4bSopenharmony_ci        const auto ext = PathUtil::GetExtension(resolvedFile);
2518bf80f4bSopenharmony_ci        const auto type = assetManager_.GetExtensionType(ext);
2528bf80f4bSopenharmony_ci        // TODO: Separate different loaders and make it possible to register more.
2538bf80f4bSopenharmony_ci        switch (type) {
2548bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::COLLECTION: {
2558bf80f4bSopenharmony_ci                ec_.SetType("entitycollection");
2568bf80f4bSopenharmony_ci                if (LoadJsonEntityCollection()) {
2578bf80f4bSopenharmony_ci                    MigrateAnimation(ec_);
2588bf80f4bSopenharmony_ci                }
2598bf80f4bSopenharmony_ci                break;
2608bf80f4bSopenharmony_ci            }
2618bf80f4bSopenharmony_ci
2628bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::SCENE: {
2638bf80f4bSopenharmony_ci                ec_.SetType("scene");
2648bf80f4bSopenharmony_ci                if (LoadJsonEntityCollection()) {
2658bf80f4bSopenharmony_ci                    MigrateAnimation(ec_);
2668bf80f4bSopenharmony_ci                }
2678bf80f4bSopenharmony_ci                break;
2688bf80f4bSopenharmony_ci            }
2698bf80f4bSopenharmony_ci
2708bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::ANIMATION: {
2718bf80f4bSopenharmony_ci                ec_.SetType("animation");
2728bf80f4bSopenharmony_ci                if (LoadJsonEntityCollection()) {
2738bf80f4bSopenharmony_ci                    MigrateAnimation(ec_);
2748bf80f4bSopenharmony_ci                }
2758bf80f4bSopenharmony_ci                break;
2768bf80f4bSopenharmony_ci            }
2778bf80f4bSopenharmony_ci
2788bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::MATERIAL: {
2798bf80f4bSopenharmony_ci                ec_.SetType("material");
2808bf80f4bSopenharmony_ci                LoadJsonEntityCollection();
2818bf80f4bSopenharmony_ci                break;
2828bf80f4bSopenharmony_ci            }
2838bf80f4bSopenharmony_ci
2848bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::GLTF:
2858bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::GLB: {
2868bf80f4bSopenharmony_ci                ec_.SetType("gltf");
2878bf80f4bSopenharmony_ci                LoadGltfEntityCollection();
2888bf80f4bSopenharmony_ci                break;
2898bf80f4bSopenharmony_ci            }
2908bf80f4bSopenharmony_ci
2918bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::JPG:
2928bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::PNG:
2938bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::KTX: {
2948bf80f4bSopenharmony_ci                ec_.SetType("image");
2958bf80f4bSopenharmony_ci                LoadImageEntityCollection();
2968bf80f4bSopenharmony_ci                break;
2978bf80f4bSopenharmony_ci            }
2988bf80f4bSopenharmony_ci
2998bf80f4bSopenharmony_ci            case IAssetManager::ExtensionType::NOT_SUPPORTED:
3008bf80f4bSopenharmony_ci            default: {
3018bf80f4bSopenharmony_ci                CORE_LOG_E("Unsupported asset format: '%s'", src_.c_str());
3028bf80f4bSopenharmony_ci                result_ = { false, "Unsupported asset format" };
3038bf80f4bSopenharmony_ci                done_ = true;
3048bf80f4bSopenharmony_ci                break;
3058bf80f4bSopenharmony_ci            }
3068bf80f4bSopenharmony_ci        }
3078bf80f4bSopenharmony_ci
3088bf80f4bSopenharmony_ci        ContinueLoading();
3098bf80f4bSopenharmony_ci    }
3108bf80f4bSopenharmony_ci
3118bf80f4bSopenharmony_ci    void ContinueLoading()
3128bf80f4bSopenharmony_ci    {
3138bf80f4bSopenharmony_ci        if (done_) {
3148bf80f4bSopenharmony_ci            ec_.MarkModified(false, true);
3158bf80f4bSopenharmony_ci
3168bf80f4bSopenharmony_ci            for (auto* listener : listeners_) {
3178bf80f4bSopenharmony_ci                listener->OnLoadFinished(*this);
3188bf80f4bSopenharmony_ci            }
3198bf80f4bSopenharmony_ci            return;
3208bf80f4bSopenharmony_ci        }
3218bf80f4bSopenharmony_ci
3228bf80f4bSopenharmony_ci        auto* dep = GetNextDependency();
3238bf80f4bSopenharmony_ci        if (dep) {
3248bf80f4bSopenharmony_ci            if (async_) {
3258bf80f4bSopenharmony_ci                dep->LoadAssetAsync();
3268bf80f4bSopenharmony_ci            } else {
3278bf80f4bSopenharmony_ci                dep->LoadAsset();
3288bf80f4bSopenharmony_ci            }
3298bf80f4bSopenharmony_ci            return;
3308bf80f4bSopenharmony_ci        }
3318bf80f4bSopenharmony_ci    }
3328bf80f4bSopenharmony_ci
3338bf80f4bSopenharmony_ci    void CreateDummyEntity()
3348bf80f4bSopenharmony_ci    {
3358bf80f4bSopenharmony_ci        // Create a dummy root entity as a placeholder for a missing asset.
3368bf80f4bSopenharmony_ci        auto dummyEntity = ecs_.GetEntityManager().CreateReferenceCounted();
3378bf80f4bSopenharmony_ci        // Note: adding a node component for now so it will be displayed in the hierarchy pane.
3388bf80f4bSopenharmony_ci        // This is wrong as the failed asset might not be a 3D node in reality. this could be removed after combining
3398bf80f4bSopenharmony_ci        // the Entity pane functionlity to the hierarchy pane and when there is better handling for missing references.
3408bf80f4bSopenharmony_ci        auto* nodeComponentManager = GetManager<INodeComponentManager>(ecs_);
3418bf80f4bSopenharmony_ci        CORE_ASSERT(nodeComponentManager);
3428bf80f4bSopenharmony_ci        if (nodeComponentManager) {
3438bf80f4bSopenharmony_ci            nodeComponentManager->Create(dummyEntity);
3448bf80f4bSopenharmony_ci        }
3458bf80f4bSopenharmony_ci        if (!GetUri().empty()) {
3468bf80f4bSopenharmony_ci            auto* nameComponentManager = GetManager<INameComponentManager>(ecs_);
3478bf80f4bSopenharmony_ci            CORE_ASSERT(nameComponentManager);
3488bf80f4bSopenharmony_ci            if (nameComponentManager) {
3498bf80f4bSopenharmony_ci                nameComponentManager->Create(dummyEntity);
3508bf80f4bSopenharmony_ci                nameComponentManager->Write(dummyEntity)->name = GetUri();
3518bf80f4bSopenharmony_ci            }
3528bf80f4bSopenharmony_ci        }
3538bf80f4bSopenharmony_ci        ec_.AddEntity(dummyEntity);
3548bf80f4bSopenharmony_ci        ec_.SetId("/", dummyEntity);
3558bf80f4bSopenharmony_ci    }
3568bf80f4bSopenharmony_ci
3578bf80f4bSopenharmony_ci    bool LoadJsonEntityCollection()
3588bf80f4bSopenharmony_ci    {
3598bf80f4bSopenharmony_ci        const auto resolvedUri = GetUri();
3608bf80f4bSopenharmony_ci        const auto resolvedFile = PathUtil::ResolveUri(contextUri_, src_, false);
3618bf80f4bSopenharmony_ci
3628bf80f4bSopenharmony_ci        const auto params = PathUtil::GetUriParameters(src_);
3638bf80f4bSopenharmony_ci        const auto targetParam = params.find("target");
3648bf80f4bSopenharmony_ci        string entityTarget = (targetParam != params.cend()) ? targetParam->second : "";
3658bf80f4bSopenharmony_ci        auto targetEntity = ec_.GetEntity(entityTarget);
3668bf80f4bSopenharmony_ci        size_t subcollectionCount = ec_.GetSubCollectionCount();
3678bf80f4bSopenharmony_ci        while (!targetEntity && subcollectionCount) {
3688bf80f4bSopenharmony_ci            targetEntity = ec_.GetSubCollection(--subcollectionCount)->GetEntity(entityTarget);
3698bf80f4bSopenharmony_ci        }
3708bf80f4bSopenharmony_ci
3718bf80f4bSopenharmony_ci        string textIn;
3728bf80f4bSopenharmony_ci        if (IoUtil::LoadTextFile(resolvedFile, textIn, fileManager_)) {
3738bf80f4bSopenharmony_ci            // TODO: Check file version here.
3748bf80f4bSopenharmony_ci
3758bf80f4bSopenharmony_ci            auto json = json::parse(textIn.data());
3768bf80f4bSopenharmony_ci            if (!json) {
3778bf80f4bSopenharmony_ci                CORE_LOG_E("Parsing json failed: '%s':\n%s", resolvedUri.c_str(), textIn.c_str());
3788bf80f4bSopenharmony_ci            } else {
3798bf80f4bSopenharmony_ci                if (!dependencies_.empty()) {
3808bf80f4bSopenharmony_ci                    // There were dependencies loaded earlier and now we want to load the actual asset.
3818bf80f4bSopenharmony_ci                    // No dependencies to load. Just load the entity collection itself and this loading is done.
3828bf80f4bSopenharmony_ci                    auto result = assetManager_.GetEcsSerializer().ReadEntityCollection(ec_, json, resolvedUri);
3838bf80f4bSopenharmony_ci                    done_ = true;
3848bf80f4bSopenharmony_ci                    return result;
3858bf80f4bSopenharmony_ci                }
3868bf80f4bSopenharmony_ci
3878bf80f4bSopenharmony_ci                vector<IEcsSerializer::ExternalCollection> dependencies;
3888bf80f4bSopenharmony_ci                assetManager_.GetEcsSerializer().GatherExternalCollections(json, resolvedUri, dependencies);
3898bf80f4bSopenharmony_ci
3908bf80f4bSopenharmony_ci                for (const auto& dep : dependencies) {
3918bf80f4bSopenharmony_ci                    BASE_NS::string patchedDepUri = dep.src;
3928bf80f4bSopenharmony_ci                    if (!entityTarget.empty())
3938bf80f4bSopenharmony_ci                        patchedDepUri.append("?target=").append(entityTarget);
3948bf80f4bSopenharmony_ci                    if (!assetManager_.IsCachedCollection(patchedDepUri, dep.contextUri)) {
3958bf80f4bSopenharmony_ci                        auto* cacheEc =
3968bf80f4bSopenharmony_ci                            assetManager_.CreateCachedCollection(ec_.GetEcs(), patchedDepUri, dep.contextUri);
3978bf80f4bSopenharmony_ci                        dependencies_.emplace_back(
3988bf80f4bSopenharmony_ci                            assetManager_.CreateAssetLoader(*cacheEc, patchedDepUri, dep.contextUri));
3998bf80f4bSopenharmony_ci                        auto& dep = *dependencies_.back();
4008bf80f4bSopenharmony_ci                        dep.AddListener(*this);
4018bf80f4bSopenharmony_ci                    }
4028bf80f4bSopenharmony_ci                }
4038bf80f4bSopenharmony_ci
4048bf80f4bSopenharmony_ci                if (GetNextDependency() == nullptr) {
4058bf80f4bSopenharmony_ci                    // No dependencies to load. Just load the entity collection itself and this loading is done.
4068bf80f4bSopenharmony_ci                    auto result = assetManager_.GetEcsSerializer().ReadEntityCollection(ec_, json, resolvedUri);
4078bf80f4bSopenharmony_ci                    done_ = true;
4088bf80f4bSopenharmony_ci                    return result;
4098bf80f4bSopenharmony_ci                }
4108bf80f4bSopenharmony_ci
4118bf80f4bSopenharmony_ci                // There are dependencies that need to be parsed in a next step.
4128bf80f4bSopenharmony_ci                return true;
4138bf80f4bSopenharmony_ci            }
4148bf80f4bSopenharmony_ci        }
4158bf80f4bSopenharmony_ci
4168bf80f4bSopenharmony_ci        CreateDummyEntity();
4178bf80f4bSopenharmony_ci        result_ = { false, "collection loading failed." };
4188bf80f4bSopenharmony_ci        done_ = true;
4198bf80f4bSopenharmony_ci        return false;
4208bf80f4bSopenharmony_ci    }
4218bf80f4bSopenharmony_ci
4228bf80f4bSopenharmony_ci    void LoadGltfEntityCollection()
4238bf80f4bSopenharmony_ci    {
4248bf80f4bSopenharmony_ci        const auto resolvedFile = PathUtil::ResolveUri(contextUri_, src_, false);
4258bf80f4bSopenharmony_ci
4268bf80f4bSopenharmony_ci        auto& gltf = graphicsContext_.GetGltf();
4278bf80f4bSopenharmony_ci
4288bf80f4bSopenharmony_ci        loadResult_ = gltf.LoadGLTF(resolvedFile);
4298bf80f4bSopenharmony_ci        if (!loadResult_.success) {
4308bf80f4bSopenharmony_ci            CORE_LOG_E("Loaded '%s' with errors:\n%s", resolvedFile.c_str(), loadResult_.error.c_str());
4318bf80f4bSopenharmony_ci        }
4328bf80f4bSopenharmony_ci        if (!loadResult_.data) {
4338bf80f4bSopenharmony_ci            CreateDummyEntity();
4348bf80f4bSopenharmony_ci            result_ = { false, "glTF load failed." };
4358bf80f4bSopenharmony_ci            done_ = true;
4368bf80f4bSopenharmony_ci            return;
4378bf80f4bSopenharmony_ci        }
4388bf80f4bSopenharmony_ci
4398bf80f4bSopenharmony_ci        importer_ = gltf.CreateGLTF2Importer(ec_.GetEcs());
4408bf80f4bSopenharmony_ci
4418bf80f4bSopenharmony_ci        if (async_) {
4428bf80f4bSopenharmony_ci            importer_->ImportGLTFAsync(*loadResult_.data, CORE_GLTF_IMPORT_RESOURCE_FLAG_BITS_ALL, this);
4438bf80f4bSopenharmony_ci        } else {
4448bf80f4bSopenharmony_ci            importer_->ImportGLTF(*loadResult_.data, CORE_GLTF_IMPORT_RESOURCE_FLAG_BITS_ALL);
4458bf80f4bSopenharmony_ci            OnImportFinished();
4468bf80f4bSopenharmony_ci        }
4478bf80f4bSopenharmony_ci    }
4488bf80f4bSopenharmony_ci
4498bf80f4bSopenharmony_ci    Entity ImportSceneFromGltf(EntityReference root)
4508bf80f4bSopenharmony_ci    {
4518bf80f4bSopenharmony_ci        auto& gltf = graphicsContext_.GetGltf();
4528bf80f4bSopenharmony_ci
4538bf80f4bSopenharmony_ci        // Import the default scene, or first scene if there is no default scene set.
4548bf80f4bSopenharmony_ci        size_t sceneIndex = loadResult_.data->GetDefaultSceneIndex();
4558bf80f4bSopenharmony_ci        if (sceneIndex == CORE_GLTF_INVALID_INDEX && loadResult_.data->GetSceneCount() > 0) {
4568bf80f4bSopenharmony_ci            // Use first scene.
4578bf80f4bSopenharmony_ci            sceneIndex = 0;
4588bf80f4bSopenharmony_ci        }
4598bf80f4bSopenharmony_ci
4608bf80f4bSopenharmony_ci        const CORE3D_NS::GLTFResourceData& resourceData = importer_->GetResult().data;
4618bf80f4bSopenharmony_ci        Entity importedSceneEntity {};
4628bf80f4bSopenharmony_ci        if (sceneIndex != CORE_GLTF_INVALID_INDEX) {
4638bf80f4bSopenharmony_ci            GltfSceneImportFlags importFlags = CORE_GLTF_IMPORT_COMPONENT_FLAG_BITS_ALL;
4648bf80f4bSopenharmony_ci            importedSceneEntity =
4658bf80f4bSopenharmony_ci                gltf.ImportGltfScene(sceneIndex, *loadResult_.data, resourceData, ecs_, root, importFlags);
4668bf80f4bSopenharmony_ci        }
4678bf80f4bSopenharmony_ci        if (!EntityUtil::IsValid(importedSceneEntity)) {
4688bf80f4bSopenharmony_ci            return {};
4698bf80f4bSopenharmony_ci        }
4708bf80f4bSopenharmony_ci
4718bf80f4bSopenharmony_ci        // make sure everyone has a name (rest of the system fails if there are unnamed nodes)
4728bf80f4bSopenharmony_ci        auto* nodeComponentManager = GetManager<INodeComponentManager>(ecs_);
4738bf80f4bSopenharmony_ci        for (auto i = 0; i < nodeComponentManager->GetComponentCount(); i++) {
4748bf80f4bSopenharmony_ci            auto ent = nodeComponentManager->GetEntity(i);
4758bf80f4bSopenharmony_ci            auto* ncm = GetManager<INameComponentManager>(ecs_);
4768bf80f4bSopenharmony_ci            bool setName = true;
4778bf80f4bSopenharmony_ci            if (ncm->HasComponent(ent)) {
4788bf80f4bSopenharmony_ci                // check if empty
4798bf80f4bSopenharmony_ci                auto name = ncm->Get(ent).name;
4808bf80f4bSopenharmony_ci                if (!name.empty()) {
4818bf80f4bSopenharmony_ci                    // it has a non empty name..
4828bf80f4bSopenharmony_ci                    setName = false;
4838bf80f4bSopenharmony_ci                }
4848bf80f4bSopenharmony_ci            }
4858bf80f4bSopenharmony_ci            if (setName) {
4868bf80f4bSopenharmony_ci                // no name component, so create one create one.
4878bf80f4bSopenharmony_ci                char buf[256];
4888bf80f4bSopenharmony_ci                // possibly not unique enough. user created names could conflict.
4898bf80f4bSopenharmony_ci                sprintf(buf, "Unnamed Node %d", i);
4908bf80f4bSopenharmony_ci                ncm->Set(ent, { buf });
4918bf80f4bSopenharmony_ci            }
4928bf80f4bSopenharmony_ci        }
4938bf80f4bSopenharmony_ci
4948bf80f4bSopenharmony_ci        // Link animation tracks to targets
4958bf80f4bSopenharmony_ci        if (!resourceData.animations.empty()) {
4968bf80f4bSopenharmony_ci            INodeSystem* nodeSystem = GetSystem<INodeSystem>(ecs_);
4978bf80f4bSopenharmony_ci            if (auto animationRootNode = nodeSystem->GetNode(importedSceneEntity); animationRootNode) {
4988bf80f4bSopenharmony_ci                for (const auto& animationEntity : resourceData.animations) {
4998bf80f4bSopenharmony_ci                    UpdateTrackTargets(animationEntity, animationRootNode);
5008bf80f4bSopenharmony_ci                }
5018bf80f4bSopenharmony_ci            }
5028bf80f4bSopenharmony_ci        }
5038bf80f4bSopenharmony_ci
5048bf80f4bSopenharmony_ci        return importedSceneEntity;
5058bf80f4bSopenharmony_ci    }
5068bf80f4bSopenharmony_ci
5078bf80f4bSopenharmony_ci    void UpdateTrackTargets(Entity animationEntity, ISceneNode* node)
5088bf80f4bSopenharmony_ci    {
5098bf80f4bSopenharmony_ci        auto& nameManager_ = *GetManager<INameComponentManager>(ecs_);
5108bf80f4bSopenharmony_ci        auto animationManager = GetManager<IAnimationComponentManager>(ecs_);
5118bf80f4bSopenharmony_ci        auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs_);
5128bf80f4bSopenharmony_ci        auto& entityManager = ecs_.GetEntityManager();
5138bf80f4bSopenharmony_ci
5148bf80f4bSopenharmony_ci        if (const ScopedHandle<const AnimationComponent> animationData = animationManager->Read(animationEntity);
5158bf80f4bSopenharmony_ci            animationData) {
5168bf80f4bSopenharmony_ci            vector<Entity> targetEntities;
5178bf80f4bSopenharmony_ci            targetEntities.reserve(animationData->tracks.size());
5188bf80f4bSopenharmony_ci            std::transform(animationData->tracks.begin(), animationData->tracks.end(),
5198bf80f4bSopenharmony_ci                std::back_inserter(targetEntities), [&nameManager = nameManager_, &node](const Entity& trackEntity) {
5208bf80f4bSopenharmony_ci                    if (auto nameHandle = nameManager.Read(trackEntity); nameHandle) {
5218bf80f4bSopenharmony_ci                        if (auto targetNode = node->LookupNodeByPath(nameHandle->name); targetNode) {
5228bf80f4bSopenharmony_ci                            return targetNode->GetEntity();
5238bf80f4bSopenharmony_ci                        }
5248bf80f4bSopenharmony_ci                    }
5258bf80f4bSopenharmony_ci                    return Entity {};
5268bf80f4bSopenharmony_ci                });
5278bf80f4bSopenharmony_ci            if (animationData->tracks.size() == targetEntities.size()) {
5288bf80f4bSopenharmony_ci                auto targetIt = targetEntities.begin();
5298bf80f4bSopenharmony_ci                for (const auto& trackEntity : animationData->tracks) {
5308bf80f4bSopenharmony_ci                    if (auto track = animationTrackManager->Write(trackEntity); track) {
5318bf80f4bSopenharmony_ci                        if (track->target) {
5328bf80f4bSopenharmony_ci                            SCENE_PLUGIN_VERBOSE_LOG("AnimationTrack %s already targetted",
5338bf80f4bSopenharmony_ci                                to_hex(static_cast<const Entity&>(track->target).id).data());
5348bf80f4bSopenharmony_ci                        }
5358bf80f4bSopenharmony_ci                        track->target = entityManager.GetReferenceCounted(*targetIt);
5368bf80f4bSopenharmony_ci                    }
5378bf80f4bSopenharmony_ci                    ++targetIt;
5388bf80f4bSopenharmony_ci                }
5398bf80f4bSopenharmony_ci            }
5408bf80f4bSopenharmony_ci        }
5418bf80f4bSopenharmony_ci    }
5428bf80f4bSopenharmony_ci
5438bf80f4bSopenharmony_ci    void GltfImportFinished()
5448bf80f4bSopenharmony_ci    {
5458bf80f4bSopenharmony_ci        auto* nodeSystem = GetSystem<INodeSystem>(ecs_);
5468bf80f4bSopenharmony_ci        CORE_ASSERT(nodeSystem);
5478bf80f4bSopenharmony_ci        auto* nodeComponentManager = GetManager<INodeComponentManager>(ecs_);
5488bf80f4bSopenharmony_ci        CORE_ASSERT(nodeComponentManager);
5498bf80f4bSopenharmony_ci        if (!nodeSystem || !nodeComponentManager) {
5508bf80f4bSopenharmony_ci            result_ = { false, {} };
5518bf80f4bSopenharmony_ci            done_ = true;
5528bf80f4bSopenharmony_ci            return;
5538bf80f4bSopenharmony_ci        }
5548bf80f4bSopenharmony_ci
5558bf80f4bSopenharmony_ci        const auto params = PathUtil::GetUriParameters(src_);
5568bf80f4bSopenharmony_ci        const auto rootParam = params.find("root");
5578bf80f4bSopenharmony_ci        string entityPath = (rootParam != params.cend()) ? rootParam->second : "";
5588bf80f4bSopenharmony_ci
5598bf80f4bSopenharmony_ci        const auto targetParam = params.find("target");
5608bf80f4bSopenharmony_ci        string entityTarget = (targetParam != params.cend()) ? targetParam->second : "";
5618bf80f4bSopenharmony_ci        auto targetEntity = ec_.GetEntity(entityTarget);
5628bf80f4bSopenharmony_ci        size_t subcollectionCount = ec_.GetSubCollectionCount();
5638bf80f4bSopenharmony_ci        while (!targetEntity && subcollectionCount) {
5648bf80f4bSopenharmony_ci            targetEntity = ec_.GetSubCollection(--subcollectionCount)->GetEntity(entityTarget);
5658bf80f4bSopenharmony_ci        }
5668bf80f4bSopenharmony_ci
5678bf80f4bSopenharmony_ci        if (importer_) {
5688bf80f4bSopenharmony_ci            auto const gltfImportResult = importer_->GetResult();
5698bf80f4bSopenharmony_ci            if (!gltfImportResult.success) {
5708bf80f4bSopenharmony_ci                CORE_LOG_E("Importing of '%s' failed: %s", GetUri().c_str(), gltfImportResult.error.c_str());
5718bf80f4bSopenharmony_ci                CreateDummyEntity();
5728bf80f4bSopenharmony_ci                result_ = { false, "glTF import failed." };
5738bf80f4bSopenharmony_ci                done_ = true;
5748bf80f4bSopenharmony_ci                return;
5758bf80f4bSopenharmony_ci
5768bf80f4bSopenharmony_ci            } else if (cancelled_) {
5778bf80f4bSopenharmony_ci                CORE_LOG_V("Importing of '%s' cancelled", GetUri().c_str());
5788bf80f4bSopenharmony_ci                CreateDummyEntity();
5798bf80f4bSopenharmony_ci                result_ = { false, "glTF import cancelled." };
5808bf80f4bSopenharmony_ci                done_ = true;
5818bf80f4bSopenharmony_ci                return;
5828bf80f4bSopenharmony_ci            }
5838bf80f4bSopenharmony_ci
5848bf80f4bSopenharmony_ci            // Loading and importing of glTF was done successfully. Fill the collection with all the gltf entities.
5858bf80f4bSopenharmony_ci            const auto originalRootEntity = ImportSceneFromGltf(targetEntity);
5868bf80f4bSopenharmony_ci            auto* originalRootNode = nodeSystem->GetNode(originalRootEntity);
5878bf80f4bSopenharmony_ci
5888bf80f4bSopenharmony_ci            // It's possible to only add some specific node from the gltf.
5898bf80f4bSopenharmony_ci            auto* loadNode = originalRootNode;
5908bf80f4bSopenharmony_ci            if (!entityPath.empty()) {
5918bf80f4bSopenharmony_ci                loadNode = originalRootNode->LookupNodeByPath(entityPath);
5928bf80f4bSopenharmony_ci                if (!loadNode || loadNode->GetEntity() == Entity {}) {
5938bf80f4bSopenharmony_ci                    CORE_LOG_E("Entity '%s' not found from '%s'", entityPath.c_str(), GetUri().c_str());
5948bf80f4bSopenharmony_ci                }
5958bf80f4bSopenharmony_ci            }
5968bf80f4bSopenharmony_ci
5978bf80f4bSopenharmony_ci            if (!loadNode || loadNode->GetEntity() == Entity {}) {
5988bf80f4bSopenharmony_ci                CreateDummyEntity();
5998bf80f4bSopenharmony_ci                result_ = { false, "Ivalid uri" };
6008bf80f4bSopenharmony_ci                done_ = true;
6018bf80f4bSopenharmony_ci                return;
6028bf80f4bSopenharmony_ci            }
6038bf80f4bSopenharmony_ci
6048bf80f4bSopenharmony_ci            Entity entity = loadNode->GetEntity();
6058bf80f4bSopenharmony_ci            if (entity != Entity {}) {
6068bf80f4bSopenharmony_ci                EntityReference ref = ecs_.GetEntityManager().GetReferenceCounted(entity);
6078bf80f4bSopenharmony_ci                ec_.AddEntity(ref);
6088bf80f4bSopenharmony_ci                ec_.SetId("/", ref);
6098bf80f4bSopenharmony_ci
6108bf80f4bSopenharmony_ci                if (!targetEntity) {
6118bf80f4bSopenharmony_ci                    loadNode->SetParent(nodeSystem->GetRootNode());
6128bf80f4bSopenharmony_ci                }
6138bf80f4bSopenharmony_ci                for (auto* child : loadNode->GetChildren()) {
6148bf80f4bSopenharmony_ci                    AddNodeToCollectionRecursive(ec_, *child, "/");
6158bf80f4bSopenharmony_ci                }
6168bf80f4bSopenharmony_ci            }
6178bf80f4bSopenharmony_ci
6188bf80f4bSopenharmony_ci            // TODO: a little backwards to first create everything and then delete the extra.
6198bf80f4bSopenharmony_ci            if (entity != originalRootEntity) {
6208bf80f4bSopenharmony_ci                auto* oldRoot = nodeSystem->GetNode(originalRootEntity);
6218bf80f4bSopenharmony_ci                CORE_ASSERT(oldRoot);
6228bf80f4bSopenharmony_ci                if (oldRoot) {
6238bf80f4bSopenharmony_ci                    nodeSystem->DestroyNode(*oldRoot);
6248bf80f4bSopenharmony_ci                }
6258bf80f4bSopenharmony_ci            }
6268bf80f4bSopenharmony_ci
6278bf80f4bSopenharmony_ci            // Add all resources in separate sub-collections. Not just 3D nodes.
6288bf80f4bSopenharmony_ci            {
6298bf80f4bSopenharmony_ci                const auto& importResult = importer_->GetResult();
6308bf80f4bSopenharmony_ci                ec_.AddSubCollection("images", {}).AddEntities(importResult.data.images);
6318bf80f4bSopenharmony_ci                ec_.AddSubCollection("materials", {}).AddEntities(importResult.data.materials);
6328bf80f4bSopenharmony_ci                ec_.AddSubCollection("meshes", {}).AddEntities(importResult.data.meshes);
6338bf80f4bSopenharmony_ci                ec_.AddSubCollection("skins", {}).AddEntities(importResult.data.skins);
6348bf80f4bSopenharmony_ci                ec_.AddSubCollection("animations", {}).AddEntities(importResult.data.animations);
6358bf80f4bSopenharmony_ci
6368bf80f4bSopenharmony_ci                // TODO: don't list duplicates
6378bf80f4bSopenharmony_ci                vector<EntityReference> animationTracks;
6388bf80f4bSopenharmony_ci                auto* acm = GetManager<IAnimationComponentManager>(ecs_);
6398bf80f4bSopenharmony_ci                if (acm) {
6408bf80f4bSopenharmony_ci                    for (auto& entity : importResult.data.animations) {
6418bf80f4bSopenharmony_ci                        if (auto handle = acm->Read(entity); handle) {
6428bf80f4bSopenharmony_ci                            const auto& tracks = handle->tracks;
6438bf80f4bSopenharmony_ci                            for (auto& entityRef : tracks) {
6448bf80f4bSopenharmony_ci                                animationTracks.emplace_back(entityRef);
6458bf80f4bSopenharmony_ci                            }
6468bf80f4bSopenharmony_ci                        }
6478bf80f4bSopenharmony_ci                    }
6488bf80f4bSopenharmony_ci                }
6498bf80f4bSopenharmony_ci                ec_.AddSubCollection("animationTracks", {}).AddEntities(animationTracks);
6508bf80f4bSopenharmony_ci            }
6518bf80f4bSopenharmony_ci
6528bf80f4bSopenharmony_ci            // Load finished successfully.
6538bf80f4bSopenharmony_ci            done_ = true;
6548bf80f4bSopenharmony_ci        }
6558bf80f4bSopenharmony_ci    }
6568bf80f4bSopenharmony_ci
6578bf80f4bSopenharmony_ci    bool LoadImageEntityCollection()
6588bf80f4bSopenharmony_ci    {
6598bf80f4bSopenharmony_ci        auto uri = GetUri();
6608bf80f4bSopenharmony_ci        auto imageHandle = assetManager_.GetEcsSerializer().LoadImageResource(uri);
6618bf80f4bSopenharmony_ci
6628bf80f4bSopenharmony_ci        // NOTE: Creating the entity even when the image load failed to load so we can detect a missing resource.
6638bf80f4bSopenharmony_ci        EntityReference entity = ecs_.GetEntityManager().CreateReferenceCounted();
6648bf80f4bSopenharmony_ci        ec_.AddEntity(entity);
6658bf80f4bSopenharmony_ci
6668bf80f4bSopenharmony_ci        auto* ncm = GetManager<INameComponentManager>(ecs_);
6678bf80f4bSopenharmony_ci        auto* ucm = GetManager<IUriComponentManager>(ecs_);
6688bf80f4bSopenharmony_ci        auto* gcm = GetManager<IRenderHandleComponentManager>(ecs_);
6698bf80f4bSopenharmony_ci        if (ncm && ucm && gcm) {
6708bf80f4bSopenharmony_ci            {
6718bf80f4bSopenharmony_ci                ncm->Create(entity);
6728bf80f4bSopenharmony_ci                auto nc = ncm->Get(entity);
6738bf80f4bSopenharmony_ci                nc.name = PathUtil::GetFilename(uri);
6748bf80f4bSopenharmony_ci                ec_.SetUniqueIdentifier(nc.name, entity);
6758bf80f4bSopenharmony_ci                ncm->Set(entity, nc);
6768bf80f4bSopenharmony_ci            }
6778bf80f4bSopenharmony_ci
6788bf80f4bSopenharmony_ci            {
6798bf80f4bSopenharmony_ci                // TODO: maybe need to save uri + contextUri
6808bf80f4bSopenharmony_ci                ucm->Create(entity);
6818bf80f4bSopenharmony_ci                auto uc = ucm->Get(entity);
6828bf80f4bSopenharmony_ci                uc.uri = uri;
6838bf80f4bSopenharmony_ci                ucm->Set(entity, uc);
6848bf80f4bSopenharmony_ci            }
6858bf80f4bSopenharmony_ci
6868bf80f4bSopenharmony_ci            gcm->Create(entity);
6878bf80f4bSopenharmony_ci            if (imageHandle) {
6888bf80f4bSopenharmony_ci                auto ic = gcm->Get(entity);
6898bf80f4bSopenharmony_ci                ic.reference = imageHandle;
6908bf80f4bSopenharmony_ci                gcm->Set(entity, ic);
6918bf80f4bSopenharmony_ci
6928bf80f4bSopenharmony_ci                done_ = true;
6938bf80f4bSopenharmony_ci                return true;
6948bf80f4bSopenharmony_ci            }
6958bf80f4bSopenharmony_ci        }
6968bf80f4bSopenharmony_ci
6978bf80f4bSopenharmony_ci        // NOTE: Always returning true as even when this fails a placeholder entity is created.
6988bf80f4bSopenharmony_ci        CORE_LOG_E("Error creating image '%s'", uri.c_str());
6998bf80f4bSopenharmony_ci        CreateDummyEntity();
7008bf80f4bSopenharmony_ci        result_ = { false, "Error creating image" };
7018bf80f4bSopenharmony_ci        done_ = true;
7028bf80f4bSopenharmony_ci        return true;
7038bf80f4bSopenharmony_ci    }
7048bf80f4bSopenharmony_ci
7058bf80f4bSopenharmony_ciprivate:
7068bf80f4bSopenharmony_ci    AssetManager& assetManager_;
7078bf80f4bSopenharmony_ci    RENDER_NS::IRenderContext& renderContext_;
7088bf80f4bSopenharmony_ci    CORE3D_NS::IGraphicsContext& graphicsContext_;
7098bf80f4bSopenharmony_ci
7108bf80f4bSopenharmony_ci    CORE_NS::IEcs& ecs_;
7118bf80f4bSopenharmony_ci    IEntityCollection& ec_;
7128bf80f4bSopenharmony_ci    BASE_NS::string src_;
7138bf80f4bSopenharmony_ci    BASE_NS::string contextUri_;
7148bf80f4bSopenharmony_ci
7158bf80f4bSopenharmony_ci    IAssetLoader::Result result_ {};
7168bf80f4bSopenharmony_ci
7178bf80f4bSopenharmony_ci    vector<AssetLoader::Ptr> dependencies_;
7188bf80f4bSopenharmony_ci
7198bf80f4bSopenharmony_ci    bool done_ { false };
7208bf80f4bSopenharmony_ci    bool cancelled_ { false };
7218bf80f4bSopenharmony_ci    bool async_ { false };
7228bf80f4bSopenharmony_ci
7238bf80f4bSopenharmony_ci    GLTFLoadResult loadResult_ {};
7248bf80f4bSopenharmony_ci    IGLTF2Importer::Ptr importer_ {};
7258bf80f4bSopenharmony_ci
7268bf80f4bSopenharmony_ci    vector<IAssetLoader::IListener*> listeners_;
7278bf80f4bSopenharmony_ci    IFileManager& fileManager_;
7288bf80f4bSopenharmony_ci};
7298bf80f4bSopenharmony_ci
7308bf80f4bSopenharmony_ciIAssetLoader::Ptr CreateAssetLoader(AssetManager& assetManager, IRenderContext& renderContext,
7318bf80f4bSopenharmony_ci    IGraphicsContext& graphicsContext, IEntityCollection& ec, string_view src, string_view contextUri)
7328bf80f4bSopenharmony_ci{
7338bf80f4bSopenharmony_ci    return IAssetLoader::Ptr { new AssetLoader(assetManager, renderContext, graphicsContext, ec, src, contextUri) };
7348bf80f4bSopenharmony_ci}
7358bf80f4bSopenharmony_ci
7368bf80f4bSopenharmony_ciSCENE_END_NAMESPACE()
737