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#include <algorithm>
168bf80f4bSopenharmony_ci#include <functional>
178bf80f4bSopenharmony_ci#include <scene_plugin/api/camera_uid.h>
188bf80f4bSopenharmony_ci#include <scene_plugin/api/environment_uid.h>
198bf80f4bSopenharmony_ci#include <scene_plugin/api/light_uid.h>
208bf80f4bSopenharmony_ci#include <scene_plugin/api/material_uid.h>
218bf80f4bSopenharmony_ci#include <scene_plugin/api/mesh_uid.h>
228bf80f4bSopenharmony_ci#include <scene_plugin/api/node_uid.h>
238bf80f4bSopenharmony_ci#include <scene_plugin/api/scene_uid.h>
248bf80f4bSopenharmony_ci#include <scene_plugin/api/view_node_uid.h>
258bf80f4bSopenharmony_ci#include <scene_plugin/interface/intf_ecs_scene.h>
268bf80f4bSopenharmony_ci#include <scene_plugin/interface/intf_environment.h>
278bf80f4bSopenharmony_ci
288bf80f4bSopenharmony_ci#include <3d/ecs/systems/intf_node_system.h>
298bf80f4bSopenharmony_ci#include <core/io/intf_file_manager.h>
308bf80f4bSopenharmony_ci
318bf80f4bSopenharmony_ci#include <meta/api/event_handler.h>
328bf80f4bSopenharmony_ci#include <meta/ext/object_container.h>
338bf80f4bSopenharmony_ci#include <meta/interface/animation/intf_animation_controller.h>
348bf80f4bSopenharmony_ci#include <meta/interface/intf_content.h>
358bf80f4bSopenharmony_ci#include <meta/interface/intf_named.h>
368bf80f4bSopenharmony_ci#include <meta/interface/intf_object_hierarchy_observer.h>
378bf80f4bSopenharmony_ci#include <meta/interface/serialization/intf_importer.h>
388bf80f4bSopenharmony_ci
398bf80f4bSopenharmony_ci#include "intf_node_private.h"
408bf80f4bSopenharmony_ci#include "intf_resource_private.h"
418bf80f4bSopenharmony_ci#include "scene_holder.h"
428bf80f4bSopenharmony_ci#include "task_utils.h"
438bf80f4bSopenharmony_ci
448bf80f4bSopenharmony_ci// "synchronously initialize the nodes from main scene file when the scene is loaded
458bf80f4bSopenharmony_ci
468bf80f4bSopenharmony_ci// Save the project .scene file when scene object is being serialized. In practice this may override the changes on
478bf80f4bSopenharmony_ci// scene file from other sources. To be rectified further
488bf80f4bSopenharmony_ci
498bf80f4bSopenharmony_ci// Name prefab instances with IObject::GetUid(). The other option is to use fixed "prefab_instance_NN" with running
508bf80f4bSopenharmony_ci// instance number. The latter may cause trouble with .scene-files if it contains previous instances
518bf80f4bSopenharmony_ci
528bf80f4bSopenharmony_ciusing SCENE_NS::MakeTask;
538bf80f4bSopenharmony_cinamespace {
548bf80f4bSopenharmony_ciclass NotifyOnExit {
558bf80f4bSopenharmony_cipublic:
568bf80f4bSopenharmony_ci    explicit NotifyOnExit(bool notify, std::function<void()> callback) : notify_(notify), callback_(callback) {}
578bf80f4bSopenharmony_ci    virtual ~NotifyOnExit()
588bf80f4bSopenharmony_ci    {
598bf80f4bSopenharmony_ci        if (notify_) {
608bf80f4bSopenharmony_ci            callback_();
618bf80f4bSopenharmony_ci        }
628bf80f4bSopenharmony_ci    }
638bf80f4bSopenharmony_ci    bool notify_;
648bf80f4bSopenharmony_ci    std::function<void()> callback_;
658bf80f4bSopenharmony_ci};
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_ciclass SceneImpl final : public META_NS::ObjectContainerFwd<SceneImpl, SCENE_NS::ClassId::Scene, SCENE_NS::IScene,
688bf80f4bSopenharmony_ci                            SCENE_NS::IEcsScene, META_NS::IContent, META_NS::IAnimationController> {
698bf80f4bSopenharmony_ci    /// Add the animation controller stuff here. (as scene should be the controller of it's animations)
708bf80f4bSopenharmony_ci    META_FORWARD_READONLY_PROPERTY(uint32_t, Count, animationController_->Count())
718bf80f4bSopenharmony_ci    META_FORWARD_READONLY_PROPERTY(uint32_t, RunningCount, animationController_->RunningCount())
728bf80f4bSopenharmony_ci
738bf80f4bSopenharmony_ci    BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> GetAnimations() const override;
748bf80f4bSopenharmony_ci    BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> GetRunning() const override;
758bf80f4bSopenharmony_ci    bool AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation) override;
768bf80f4bSopenharmony_ci    bool RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation) override;
778bf80f4bSopenharmony_ci    void Clear() override;
788bf80f4bSopenharmony_ci    StepInfo Step(const META_NS::IClock::ConstPtr& clock) override;
798bf80f4bSopenharmony_ci    // now back to normal scene impl
808bf80f4bSopenharmony_ci
818bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(META_NS::INamed, BASE_NS::string, Name, {})
828bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(
838bf80f4bSopenharmony_ci        SCENE_NS::IScene, BASE_NS::string, SystemGraphUri, "project://assets/config/system_graph.json")
848bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(
858bf80f4bSopenharmony_ci        SCENE_NS::IScene, uint32_t, Status, SCENE_STATUS_UNINITIALIZED, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
868bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IScene, SCENE_NS::INode::Ptr, RootNode, {})
878bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, SCENE_NS::ICamera::Ptr, DefaultCamera, {})
888bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, BASE_NS::string, Uri, {})
898bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, bool, Asynchronous, false)
908bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_ARRAY_PROPERTY(SCENE_NS::IScene, SCENE_NS::IMaterial::Ptr, Materials, {})
918bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, SCENE_NS::IRenderConfiguration::Ptr, RenderConfiguration, {})
928bf80f4bSopenharmony_ci
938bf80f4bSopenharmony_ci    META_IMPLEMENT_EVENT(META_NS::IOnChanged, OnLoaded)
948bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IEcsScene, uint8_t, RenderMode, SCENE_NS::IEcsScene::RENDER_IF_DIRTY)
958bf80f4bSopenharmony_ci
968bf80f4bSopenharmony_ci    META_FORWARD_READONLY_PROPERTY(IObject::Ptr, Content, (contentImpl_ ? contentImpl_->Content() : nullptr))
978bf80f4bSopenharmony_ci    META_FORWARD_PROPERTY(bool, ContentSearchable, (contentImpl_ ? contentImpl_->ContentSearchable() : nullptr))
988bf80f4bSopenharmony_ci    META_FORWARD_PROPERTY(
998bf80f4bSopenharmony_ci        META_NS::IContentLoader::Ptr, ContentLoader, (contentImpl_ ? contentImpl_->ContentLoader() : nullptr))
1008bf80f4bSopenharmony_ci
1018bf80f4bSopenharmony_ci    void SetRenderMode()
1028bf80f4bSopenharmony_ci    {
1038bf80f4bSopenharmony_ci        AddEngineTask(MakeTask(
1048bf80f4bSopenharmony_ci                          [renderMode = META_NS::GetValue(RenderMode())](auto sceneHolder) {
1058bf80f4bSopenharmony_ci                              sceneHolder->SetRenderMode(renderMode);
1068bf80f4bSopenharmony_ci                              return false;
1078bf80f4bSopenharmony_ci                          },
1088bf80f4bSopenharmony_ci                          sceneHolder_),
1098bf80f4bSopenharmony_ci            false);
1108bf80f4bSopenharmony_ci    }
1118bf80f4bSopenharmony_ci
1128bf80f4bSopenharmony_ci    uint64_t GetCameraHandle(const SCENE_NS::ICamera::Ptr& camera)
1138bf80f4bSopenharmony_ci    {
1148bf80f4bSopenharmony_ci        if (camera) {
1158bf80f4bSopenharmony_ci            auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(camera);
1168bf80f4bSopenharmony_ci            if (ecsObject) {
1178bf80f4bSopenharmony_ci                return ecsObject->GetEntity().id;
1188bf80f4bSopenharmony_ci            }
1198bf80f4bSopenharmony_ci        }
1208bf80f4bSopenharmony_ci
1218bf80f4bSopenharmony_ci        return SCENE_NS::IScene::DEFAULT_CAMERA;
1228bf80f4bSopenharmony_ci    }
1238bf80f4bSopenharmony_ci
1248bf80f4bSopenharmony_ci    struct BitmapInfo {
1258bf80f4bSopenharmony_ci        BitmapInfo() = default;
1268bf80f4bSopenharmony_ci        BitmapInfo(SCENE_NS::IBitmap::Ptr bmp) : bitmap(bmp) {}
1278bf80f4bSopenharmony_ci        BASE_NS::Math::UVec2 size {};
1288bf80f4bSopenharmony_ci        SCENE_NS::IBitmap::Ptr bitmap {};
1298bf80f4bSopenharmony_ci        META_NS::EventHandler bitmapChanged {};
1308bf80f4bSopenharmony_ci    };
1318bf80f4bSopenharmony_ci    BASE_NS::unordered_map<uint64_t, BitmapInfo> bitmaps_;
1328bf80f4bSopenharmony_ci    BitmapInfo& GetData(const SCENE_NS::ICamera::Ptr& camera)
1338bf80f4bSopenharmony_ci    {
1348bf80f4bSopenharmony_ci        auto cameraHandle = GetCameraHandle(camera);
1358bf80f4bSopenharmony_ci        if (cameraHandle == SCENE_NS::IScene::DEFAULT_CAMERA) {
1368bf80f4bSopenharmony_ci            cameraHandle = defaultCameraHandle_;
1378bf80f4bSopenharmony_ci        }
1388bf80f4bSopenharmony_ci        auto handle = cameraHandle;
1398bf80f4bSopenharmony_ci        if (cameraHandle == SCENE_NS::IScene::DEFAULT_CAMERA && !bitmaps_.empty()) {
1408bf80f4bSopenharmony_ci            handle = defaultCameraHandle_;
1418bf80f4bSopenharmony_ci        }
1428bf80f4bSopenharmony_ci        return bitmaps_[handle];
1438bf80f4bSopenharmony_ci    }
1448bf80f4bSopenharmony_ci
1458bf80f4bSopenharmony_ci    void SetBitmap(const SCENE_NS::IBitmap::Ptr& bitmap, const SCENE_NS::ICamera::Ptr& camera) override
1468bf80f4bSopenharmony_ci    {
1478bf80f4bSopenharmony_ci        BitmapInfo& data = GetData(camera);
1488bf80f4bSopenharmony_ci        auto uiBitmap = interface_pointer_cast<SCENE_NS::IBitmap>(bitmap);
1498bf80f4bSopenharmony_ci        if (!uiBitmap) {
1508bf80f4bSopenharmony_ci            // disable bitmap override.
1518bf80f4bSopenharmony_ci            data.bitmapChanged.Unsubscribe();
1528bf80f4bSopenharmony_ci            data.bitmap = {};
1538bf80f4bSopenharmony_ci            sceneHolder_->SetCameraTarget(camera, data.size, {});
1548bf80f4bSopenharmony_ci            return;
1558bf80f4bSopenharmony_ci        }
1568bf80f4bSopenharmony_ci
1578bf80f4bSopenharmony_ci        data.bitmap = uiBitmap;
1588bf80f4bSopenharmony_ci        data.size = data.bitmap->Size()->GetValue();
1598bf80f4bSopenharmony_ci        const auto rh = data.bitmap->GetRenderHandle();
1608bf80f4bSopenharmony_ci
1618bf80f4bSopenharmony_ci        data.bitmapChanged.Subscribe(
1628bf80f4bSopenharmony_ci            uiBitmap->ResourceChanged(), META_NS::MakeCallback<META_NS::IOnChanged>([this, camera]() {
1638bf80f4bSopenharmony_ci                BitmapInfo& data = GetData(camera);
1648bf80f4bSopenharmony_ci                data.size = data.bitmap->Size()->GetValue();
1658bf80f4bSopenharmony_ci                const auto rh = data.bitmap->GetRenderHandle();
1668bf80f4bSopenharmony_ci                sceneHolder_->SetCameraTarget(camera, data.size, rh);
1678bf80f4bSopenharmony_ci            }));
1688bf80f4bSopenharmony_ci
1698bf80f4bSopenharmony_ci        META_NS::Invoke<META_NS::IOnChanged>(uiBitmap->ResourceChanged());
1708bf80f4bSopenharmony_ci    }
1718bf80f4bSopenharmony_ci
1728bf80f4bSopenharmony_ci    SCENE_NS::IBitmap::Ptr GetBitmap(bool notifyFrameDrawn, const SCENE_NS::ICamera::Ptr& camera) override
1738bf80f4bSopenharmony_ci    {
1748bf80f4bSopenharmony_ci        BitmapInfo& data = GetData(camera);
1758bf80f4bSopenharmony_ci        if (!data.bitmap) {
1768bf80f4bSopenharmony_ci            // there is no bitmap for this.
1778bf80f4bSopenharmony_ci            //  create it?
1788bf80f4bSopenharmony_ci        }
1798bf80f4bSopenharmony_ci        return data.bitmap;
1808bf80f4bSopenharmony_ci    }
1818bf80f4bSopenharmony_ci
1828bf80f4bSopenharmony_ci#define CREATE_META_INSTANCE(type, name) GetObjectRegistry().Create<META_NS::type>(META_NS::ClassId::name)
1838bf80f4bSopenharmony_ci
1848bf80f4bSopenharmony_ci    // META_NS::IContent
1858bf80f4bSopenharmony_ci    bool SetContent(const META_NS::IObject::Ptr& content) override
1868bf80f4bSopenharmony_ci    {
1878bf80f4bSopenharmony_ci        // Should be no-op because the Root Node of a scene (content in this case) can be assigned only if the scene was
1888bf80f4bSopenharmony_ci        // reloaded.
1898bf80f4bSopenharmony_ci        return false;
1908bf80f4bSopenharmony_ci    }
1918bf80f4bSopenharmony_ci
1928bf80f4bSopenharmony_ci    bool Build(const META_NS::IMetadata::Ptr& data) override
1938bf80f4bSopenharmony_ci    {
1948bf80f4bSopenharmony_ci        auto& registry = GetObjectRegistry();
1958bf80f4bSopenharmony_ci
1968bf80f4bSopenharmony_ci        animationController_ = registry.Create<META_NS::IAnimationController>(META_NS::ClassId::AnimationController);
1978bf80f4bSopenharmony_ci
1988bf80f4bSopenharmony_ci        using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
1998bf80f4bSopenharmony_ci        BASE_NS::shared_ptr<RENDER_NS::IRenderContext> rc;
2008bf80f4bSopenharmony_ci        META_NS::ITaskQueue::Ptr appQueue;
2018bf80f4bSopenharmony_ci        META_NS::ITaskQueue::Ptr engineQueue;
2028bf80f4bSopenharmony_ci
2038bf80f4bSopenharmony_ci        if (data) {
2048bf80f4bSopenharmony_ci            if (auto prp = data->GetPropertyByName<IntfPtr>("RenderContext")) {
2058bf80f4bSopenharmony_ci                rc = interface_pointer_cast<RENDER_NS::IRenderContext>(prp->GetValue());
2068bf80f4bSopenharmony_ci            }
2078bf80f4bSopenharmony_ci            if (auto prp = data->GetPropertyByName<IntfPtr>("EngineQueue")) {
2088bf80f4bSopenharmony_ci                engineQueue = interface_pointer_cast<META_NS::ITaskQueue>(prp->GetValue());
2098bf80f4bSopenharmony_ci            }
2108bf80f4bSopenharmony_ci            if (auto prp = data->GetPropertyByName<IntfPtr>("AppQueue")) {
2118bf80f4bSopenharmony_ci                appQueue = interface_pointer_cast<META_NS::ITaskQueue>(prp->GetValue());
2128bf80f4bSopenharmony_ci            }
2138bf80f4bSopenharmony_ci        }
2148bf80f4bSopenharmony_ci        if ((!rc) || (!engineQueue) || (!appQueue)) {
2158bf80f4bSopenharmony_ci            return false;
2168bf80f4bSopenharmony_ci        }
2178bf80f4bSopenharmony_ci
2188bf80f4bSopenharmony_ci        hierarchyController_ =
2198bf80f4bSopenharmony_ci            registry.Create<META_NS::IObjectHierarchyObserver>(SCENE_NS::ClassId::NodeHierarchyController);
2208bf80f4bSopenharmony_ci
2218bf80f4bSopenharmony_ci        contentImpl_ = registry.Create<META_NS::IContent>(META_NS::ClassId::ContentObject);
2228bf80f4bSopenharmony_ci        if (const auto req = interface_cast<META_NS::IRequiredInterfaces>(contentImpl_)) {
2238bf80f4bSopenharmony_ci            req->SetRequiredInterfaces({ SCENE_NS::INode::UID });
2248bf80f4bSopenharmony_ci        }
2258bf80f4bSopenharmony_ci
2268bf80f4bSopenharmony_ci        sceneHolder_.reset(new SceneHolder(GetInstanceId(), registry, rc, appQueue, engineQueue));
2278bf80f4bSopenharmony_ci        sceneHolder_->SetOperationMode(Asynchronous()->GetValue());
2288bf80f4bSopenharmony_ci
2298bf80f4bSopenharmony_ci        asyncChangedToken_ = Asynchronous()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(
2308bf80f4bSopenharmony_ci            [](const auto& sceneHolder, const auto& async) {
2318bf80f4bSopenharmony_ci                if (sceneHolder && async) {
2328bf80f4bSopenharmony_ci                    sceneHolder->SetOperationMode(async->GetValue());
2338bf80f4bSopenharmony_ci                }
2348bf80f4bSopenharmony_ci            },
2358bf80f4bSopenharmony_ci            sceneHolder_, Asynchronous()));
2368bf80f4bSopenharmony_ci
2378bf80f4bSopenharmony_ci        renderModeChangedToken_ = RenderMode()->OnChanged()->AddHandler(
2388bf80f4bSopenharmony_ci            META_NS::MakeCallback<META_NS::IOnChanged>([weak = BASE_NS::weak_ptr(GetSelf())]() {
2398bf80f4bSopenharmony_ci                if (auto self = static_pointer_cast<SceneImpl>(weak.lock())) {
2408bf80f4bSopenharmony_ci                    self->SetRenderMode();
2418bf80f4bSopenharmony_ci                }
2428bf80f4bSopenharmony_ci            }));
2438bf80f4bSopenharmony_ci
2448bf80f4bSopenharmony_ci        sceneHolder_->SetInitializeCallback(
2458bf80f4bSopenharmony_ci            META_NS::MakeCallback<SceneHolder::ISceneInitialized>(
2468bf80f4bSopenharmony_ci                [me = BASE_NS::weak_ptr(GetSelf())](const BASE_NS::string& rootId, const BASE_NS::string& cameraId) {
2478bf80f4bSopenharmony_ci                    if (auto self = me.lock().get())
2488bf80f4bSopenharmony_ci                        static_cast<SceneImpl*>(self)->onSceneInitialized(rootId, cameraId);
2498bf80f4bSopenharmony_ci                }),
2508bf80f4bSopenharmony_ci            sceneHolder_);
2518bf80f4bSopenharmony_ci        sceneHolder_->SetSceneLoadedCallback(
2528bf80f4bSopenharmony_ci            META_NS::MakeCallback<SceneHolder::ISceneLoaded>([me = BASE_NS::weak_ptr(GetSelf())](uint32_t status) {
2538bf80f4bSopenharmony_ci                if (auto self = me.lock().get())
2548bf80f4bSopenharmony_ci                    static_cast<SceneImpl*>(self)->OnSceneLoaded(status);
2558bf80f4bSopenharmony_ci            }),
2568bf80f4bSopenharmony_ci            sceneHolder_);
2578bf80f4bSopenharmony_ci
2588bf80f4bSopenharmony_ci        sceneHolder_->Initialize(sceneHolder_);
2598bf80f4bSopenharmony_ci        sceneHolder_->SetSystemGraphUri(META_NS::GetValue(SystemGraphUri()));
2608bf80f4bSopenharmony_ci        SubscribeToPropertyChanges();
2618bf80f4bSopenharmony_ci
2628bf80f4bSopenharmony_ci        return true;
2638bf80f4bSopenharmony_ci    }
2648bf80f4bSopenharmony_ci
2658bf80f4bSopenharmony_ci    // this is not usually needed as explicit action, but the scene will pickup changes from
2668bf80f4bSopenharmony_ci    // ScenePresenters on the fly
2678bf80f4bSopenharmony_ci    void OnCameraChanged()
2688bf80f4bSopenharmony_ci    {
2698bf80f4bSopenharmony_ci        // Async, sceneholder takes over
2708bf80f4bSopenharmony_ci        if (sceneHolder_) {
2718bf80f4bSopenharmony_ci            sceneHolder_->ChangeCamera(META_ACCESS_PROPERTY(DefaultCamera)->GetValue());
2728bf80f4bSopenharmony_ci        }
2738bf80f4bSopenharmony_ci    }
2748bf80f4bSopenharmony_ci
2758bf80f4bSopenharmony_ci    // Async, sceneholder takes over
2768bf80f4bSopenharmony_ci    BASE_NS::vector<META_NS::IAnimation::Ptr> allAnims_;
2778bf80f4bSopenharmony_ci    BASE_NS::vector<META_NS::IAnimation::Ptr> GetAnimations() override
2788bf80f4bSopenharmony_ci    {
2798bf80f4bSopenharmony_ci        // adds/removes animations to cache..
2808bf80f4bSopenharmony_ci        auto tmp = allAnims_;
2818bf80f4bSopenharmony_ci        allAnims_.clear();
2828bf80f4bSopenharmony_ci        for (auto anim : sceneHolder_->GetAnimations()) {
2838bf80f4bSopenharmony_ci            if (auto i = interface_cast<META_NS::IObject>(anim)) {
2848bf80f4bSopenharmony_ci                auto name = i->GetName();
2858bf80f4bSopenharmony_ci
2868bf80f4bSopenharmony_ci                size_t ix = 0;
2878bf80f4bSopenharmony_ci                if (!RollOverPrefix(ix, name, ANIMATIONS_PREFIX)) {
2888bf80f4bSopenharmony_ci                    continue;
2898bf80f4bSopenharmony_ci                }
2908bf80f4bSopenharmony_ci
2918bf80f4bSopenharmony_ci                // check cache
2928bf80f4bSopenharmony_ci                META_NS::IAnimation::Ptr rn;
2938bf80f4bSopenharmony_ci                auto subname = name.substr(ix);
2948bf80f4bSopenharmony_ci                for (auto& a : tmp) {
2958bf80f4bSopenharmony_ci                    if (interface_cast<META_NS::IObject>(a)->GetName() == subname) {
2968bf80f4bSopenharmony_ci                        rn = a;
2978bf80f4bSopenharmony_ci                        break;
2988bf80f4bSopenharmony_ci                    }
2998bf80f4bSopenharmony_ci                }
3008bf80f4bSopenharmony_ci                if (!rn) {
3018bf80f4bSopenharmony_ci                    rn = interface_pointer_cast<META_NS::IAnimation>(
3028bf80f4bSopenharmony_ci                        CreateNode(name.substr(ix), false, SCENE_NS::ClassId::Animation.Id(),
3038bf80f4bSopenharmony_ci                            SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
3048bf80f4bSopenharmony_ci                }
3058bf80f4bSopenharmony_ci
3068bf80f4bSopenharmony_ci                allAnims_.push_back(rn);
3078bf80f4bSopenharmony_ci            }
3088bf80f4bSopenharmony_ci        }
3098bf80f4bSopenharmony_ci        return allAnims_;
3108bf80f4bSopenharmony_ci    }
3118bf80f4bSopenharmony_ci
3128bf80f4bSopenharmony_ci    META_NS::IAnimation::Ptr GetAnimation(const BASE_NS::string_view name) override
3138bf80f4bSopenharmony_ci    {
3148bf80f4bSopenharmony_ci#ifndef USE_DIRECT_ECS_ANIMATION
3158bf80f4bSopenharmony_ci
3168bf80f4bSopenharmony_ci        size_t ix = 0;
3178bf80f4bSopenharmony_ci        if (!RollOverPrefix(ix, name, ANIMATIONS_PREFIX)) {
3188bf80f4bSopenharmony_ci            return { nullptr };
3198bf80f4bSopenharmony_ci        }
3208bf80f4bSopenharmony_ci
3218bf80f4bSopenharmony_ci        // check cache
3228bf80f4bSopenharmony_ci        if (auto it = animations_.find(name.substr(ix)); it != animations_.end()) {
3238bf80f4bSopenharmony_ci            if (auto animation = it->second.lock()) {
3248bf80f4bSopenharmony_ci                return animation;
3258bf80f4bSopenharmony_ci            }
3268bf80f4bSopenharmony_ci        }
3278bf80f4bSopenharmony_ci        // Create EcsObject. When running asynchronously, we have no way of knowing if we can rely that existing
3288bf80f4bSopenharmony_ci        // animation will be found
3298bf80f4bSopenharmony_ci        auto anim = interface_pointer_cast<META_NS::IAnimation>(CreateNode(name.substr(ix), false,
3308bf80f4bSopenharmony_ci            SCENE_NS::ClassId::Animation.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
3318bf80f4bSopenharmony_ci
3328bf80f4bSopenharmony_ci        return anim;
3338bf80f4bSopenharmony_ci#else
3348bf80f4bSopenharmony_ci        auto ecsAnimation = sceneHolder_->GetAnimation(BASE_NS::string(name.data(), name.size()));
3358bf80f4bSopenharmony_ci
3368bf80f4bSopenharmony_ci        if (!ecsAnimation) {
3378bf80f4bSopenharmony_ci            ecsAnimation = GetObjectRegistry().Create<SCENE_NS::IEcsAnimation>(SCENE_NS::ClassId::EcsAnimation);
3388bf80f4bSopenharmony_ci            AddEngineTask(
3398bf80f4bSopenharmony_ci                META_NS::MakeCallable<META_NS::ITaskQueueTask>(
3408bf80f4bSopenharmony_ci                    [ecsAnimation, nameString = BASE_NS::string(name.data(), name.size()),
3418bf80f4bSopenharmony_ci                        weak = BASE_NS::weak_ptr(sceneHolder_)]() {
3428bf80f4bSopenharmony_ci                        if (auto sceneHolder = weak.lock()) {
3438bf80f4bSopenharmony_ci                            CORE_NS::Entity entity;
3448bf80f4bSopenharmony_ci                            if (sceneHolder->FindAnimation(nameString, entity)) {
3458bf80f4bSopenharmony_ci                                if (auto ecsProxyIf = interface_pointer_cast<SCENE_NS::IEcsProxyObject>(ecsAnimation)) {
3468bf80f4bSopenharmony_ci                                    ecsProxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
3478bf80f4bSopenharmony_ci                                }
3488bf80f4bSopenharmony_ci                                ecsAnimation->SetEntity(*sceneHolder->GetEcs(), entity);
3498bf80f4bSopenharmony_ci                            }
3508bf80f4bSopenharmony_ci                        }
3518bf80f4bSopenharmony_ci                        return false;
3528bf80f4bSopenharmony_ci                    }),
3538bf80f4bSopenharmony_ci                false);
3548bf80f4bSopenharmony_ci        }
3558bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Animations)->Get()->Add(ecsAnimation);
3568bf80f4bSopenharmony_ci
3578bf80f4bSopenharmony_ci        return interface_pointer_cast<META_NS::IAnimation>(ecsAnimation);
3588bf80f4bSopenharmony_ci#endif
3598bf80f4bSopenharmony_ci    }
3608bf80f4bSopenharmony_ci
3618bf80f4bSopenharmony_ci    void CreateEmpty() override
3628bf80f4bSopenharmony_ci    {
3638bf80f4bSopenharmony_ci        Load("scene://empty");
3648bf80f4bSopenharmony_ci    }
3658bf80f4bSopenharmony_ci
3668bf80f4bSopenharmony_ci    bool Load(const BASE_NS::string_view uri) override
3678bf80f4bSopenharmony_ci    {
3688bf80f4bSopenharmony_ci        if (!Name()->IsValueSet()) {
3698bf80f4bSopenharmony_ci            SetValue(Name(), "Scene");
3708bf80f4bSopenharmony_ci        }
3718bf80f4bSopenharmony_ci
3728bf80f4bSopenharmony_ci        if (!uri.empty()) {
3738bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_LOADING);
3748bf80f4bSopenharmony_ci            sceneHolder_->Load(BASE_NS::string(uri.data(), uri.size()));
3758bf80f4bSopenharmony_ci            return true;
3768bf80f4bSopenharmony_ci        }
3778bf80f4bSopenharmony_ci        return false;
3788bf80f4bSopenharmony_ci    }
3798bf80f4bSopenharmony_ci
3808bf80f4bSopenharmony_ci    void onSystemGraphUriChanged()
3818bf80f4bSopenharmony_ci    {
3828bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_LOADING);
3838bf80f4bSopenharmony_ci        sceneHolder_->SetSystemGraphUri(META_NS::GetValue(SystemGraphUri()));
3848bf80f4bSopenharmony_ci    }
3858bf80f4bSopenharmony_ci
3868bf80f4bSopenharmony_ci    void SetRenderSize(uint32_t width, uint32_t height, const SCENE_NS::ICamera::Ptr& camera) override
3878bf80f4bSopenharmony_ci    {
3888bf80f4bSopenharmony_ci        if (camera) {
3898bf80f4bSopenharmony_ci            camera->SetDefaultRenderTargetSize(width, height);
3908bf80f4bSopenharmony_ci        } else if (auto defaultCamera = META_NS::GetValue(DefaultCamera())) {
3918bf80f4bSopenharmony_ci            defaultCamera->SetDefaultRenderTargetSize(width, height);
3928bf80f4bSopenharmony_ci        }
3938bf80f4bSopenharmony_ci    }
3948bf80f4bSopenharmony_ci
3958bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr GetNode(const BASE_NS::string_view path, const BASE_NS::Uid classId,
3968bf80f4bSopenharmony_ci        SCENE_NS::INode::BuildBehavior buildBehavior) override
3978bf80f4bSopenharmony_ci    {
3988bf80f4bSopenharmony_ci        return GetNodeRecursive(path, classId, true, buildBehavior);
3998bf80f4bSopenharmony_ci    }
4008bf80f4bSopenharmony_ci
4018bf80f4bSopenharmony_ci    META_NS::ObjectId ResolveNodeTypeFromPath(const BASE_NS::string_view patchedPath, bool isNodeType)
4028bf80f4bSopenharmony_ci    {
4038bf80f4bSopenharmony_ci        // This is best effort
4048bf80f4bSopenharmony_ci        // We cannot determine the type unless ECS has probed the component
4058bf80f4bSopenharmony_ci
4068bf80f4bSopenharmony_ci        // This kind of introspection may cause materials and meshes to be treated as nodes
4078bf80f4bSopenharmony_ci        // which kind of contradicts with their normal use through API
4088bf80f4bSopenharmony_ci        auto ecs = GetEcs();
4098bf80f4bSopenharmony_ci        CORE3D_NS::INodeSystem& nodeSystem = *CORE_NS::GetSystem<CORE3D_NS::INodeSystem>(*ecs);
4108bf80f4bSopenharmony_ci        const auto& root = nodeSystem.GetRootNode();
4118bf80f4bSopenharmony_ci
4128bf80f4bSopenharmony_ci        const auto& ecsNode = root.LookupNodeByPath(patchedPath);
4138bf80f4bSopenharmony_ci        CORE_NS::Entity entity {};
4148bf80f4bSopenharmony_ci        if (ecsNode) {
4158bf80f4bSopenharmony_ci            entity = ecsNode->GetEntity();
4168bf80f4bSopenharmony_ci        } else {
4178bf80f4bSopenharmony_ci            CORE_LOG_W("%s:No entity for %s, type info not available", __func__, BASE_NS::string(patchedPath).c_str());
4188bf80f4bSopenharmony_ci            CORE_LOG_W("If you know the expected type, consider using a template or providing the class id");
4198bf80f4bSopenharmony_ci        }
4208bf80f4bSopenharmony_ci
4218bf80f4bSopenharmony_ci        // shadow camera could provide false positive, so order matters
4228bf80f4bSopenharmony_ci        if (auto lightManager = ecs->GetComponentManager(CORE3D_NS::ILightComponentManager::UID)) {
4238bf80f4bSopenharmony_ci            if (lightManager->HasComponent(entity)) {
4248bf80f4bSopenharmony_ci                return SCENE_NS::ClassId::Light;
4258bf80f4bSopenharmony_ci            }
4268bf80f4bSopenharmony_ci        }
4278bf80f4bSopenharmony_ci
4288bf80f4bSopenharmony_ci        if (auto cameraManager = ecs->GetComponentManager(CORE3D_NS::ICameraComponentManager::UID)) {
4298bf80f4bSopenharmony_ci            if (cameraManager->HasComponent(entity)) {
4308bf80f4bSopenharmony_ci                return SCENE_NS::ClassId::Camera;
4318bf80f4bSopenharmony_ci            }
4328bf80f4bSopenharmony_ci        }
4338bf80f4bSopenharmony_ci
4348bf80f4bSopenharmony_ci        if (auto envManager = ecs->GetComponentManager(CORE3D_NS::IEnvironmentComponentManager::UID)) {
4358bf80f4bSopenharmony_ci            if (envManager->HasComponent(entity)) {
4368bf80f4bSopenharmony_ci                return SCENE_NS::ClassId::Environment;
4378bf80f4bSopenharmony_ci            }
4388bf80f4bSopenharmony_ci        }
4398bf80f4bSopenharmony_ci
4408bf80f4bSopenharmony_ci        if (!isNodeType) {
4418bf80f4bSopenharmony_ci            if (auto nodeManager = ecs->GetComponentManager(CORE3D_NS::INodeComponentManager::UID)) {
4428bf80f4bSopenharmony_ci                // quirk, prefer nodes with node component treated as node)
4438bf80f4bSopenharmony_ci                if (!nodeManager->HasComponent(entity)) {
4448bf80f4bSopenharmony_ci                    if (auto meshManager = ecs->GetComponentManager(CORE3D_NS::IMeshComponentManager::UID)) {
4458bf80f4bSopenharmony_ci                        if (meshManager->HasComponent(entity)) {
4468bf80f4bSopenharmony_ci                            return SCENE_NS::ClassId::Mesh;
4478bf80f4bSopenharmony_ci                        }
4488bf80f4bSopenharmony_ci                    }
4498bf80f4bSopenharmony_ci                    if (auto materialManager = ecs->GetComponentManager(CORE3D_NS::IMaterialComponentManager::UID)) {
4508bf80f4bSopenharmony_ci                        if (materialManager->HasComponent(entity)) {
4518bf80f4bSopenharmony_ci                            return SCENE_NS::ClassId::Material;
4528bf80f4bSopenharmony_ci                        }
4538bf80f4bSopenharmony_ci                    }
4548bf80f4bSopenharmony_ci                }
4558bf80f4bSopenharmony_ci            }
4568bf80f4bSopenharmony_ci        }
4578bf80f4bSopenharmony_ci
4588bf80f4bSopenharmony_ci        return SCENE_NS::ClassId::Node;
4598bf80f4bSopenharmony_ci    }
4608bf80f4bSopenharmony_ci
4618bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr GetNodeRecursive(const BASE_NS::string_view path, const META_NS::ObjectId classId,
4628bf80f4bSopenharmony_ci        bool recurse, SCENE_NS::INode::BuildBehavior buildBehavior)
4638bf80f4bSopenharmony_ci    {
4648bf80f4bSopenharmony_ci        if (path.empty() || path == "/") {
4658bf80f4bSopenharmony_ci            return SCENE_NS::INode::Ptr {};
4668bf80f4bSopenharmony_ci        }
4678bf80f4bSopenharmony_ci
4688bf80f4bSopenharmony_ci        BASE_NS::string patchedPath = recurse ? NormalizePath(path) : BASE_NS::string(path.data(), path.size());
4698bf80f4bSopenharmony_ci        if (auto ite = nodes_.find(patchedPath) != nodes_.cend()) {
4708bf80f4bSopenharmony_ci            return nodes_[patchedPath];
4718bf80f4bSopenharmony_ci        }
4728bf80f4bSopenharmony_ci
4738bf80f4bSopenharmony_ci        // ensure parent objects exist
4748bf80f4bSopenharmony_ci        if (recurse) {
4758bf80f4bSopenharmony_ci            size_t ix = patchedPath.find('/', 1);
4768bf80f4bSopenharmony_ci            while (BASE_NS::string_view::npos != ix) {
4778bf80f4bSopenharmony_ci                auto substr = patchedPath.substr(0, ix);
4788bf80f4bSopenharmony_ci                // When we traverse up the tree, we must ensure that the object is a node.
4798bf80f4bSopenharmony_ci                currentParent_ = GetNodeRecursive(substr, SCENE_NS::INode::UID, false, buildBehavior);
4808bf80f4bSopenharmony_ci                ++ix;
4818bf80f4bSopenharmony_ci                ix = patchedPath.find('/', ix);
4828bf80f4bSopenharmony_ci            }
4838bf80f4bSopenharmony_ci        }
4848bf80f4bSopenharmony_ci
4858bf80f4bSopenharmony_ci        auto ecs = GetEcs();
4868bf80f4bSopenharmony_ci        META_NS::ObjectId implementationId = SCENE_NS::ClassId::Node;
4878bf80f4bSopenharmony_ci
4888bf80f4bSopenharmony_ci        bool isNodeType = (classId == SCENE_NS::INode::UID);
4898bf80f4bSopenharmony_ci        if ((classId == META_NS::IObject::UID || isNodeType) && ecs) {
4908bf80f4bSopenharmony_ci            implementationId = ResolveNodeTypeFromPath(patchedPath, isNodeType);
4918bf80f4bSopenharmony_ci
4928bf80f4bSopenharmony_ci        } else {
4938bf80f4bSopenharmony_ci            implementationId = classId;
4948bf80f4bSopenharmony_ci        }
4958bf80f4bSopenharmony_ci
4968bf80f4bSopenharmony_ci        auto node = CreateNode(patchedPath, false, implementationId, buildBehavior);
4978bf80f4bSopenharmony_ci
4988bf80f4bSopenharmony_ci        currentParent_ = {};
4998bf80f4bSopenharmony_ci        return node; // finalInterface;
5008bf80f4bSopenharmony_ci    }
5018bf80f4bSopenharmony_ci
5028bf80f4bSopenharmony_ci    void RemoveNodeFromCurrentContainer(SCENE_NS::INode::Ptr& node)
5038bf80f4bSopenharmony_ci    {
5048bf80f4bSopenharmony_ci        if (auto containable = interface_cast<META_NS::IContainable>(node)) {
5058bf80f4bSopenharmony_ci            if (auto parent = interface_pointer_cast<META_NS::IContainer>(containable->GetParent())) {
5068bf80f4bSopenharmony_ci                parent->Remove(node);
5078bf80f4bSopenharmony_ci            }
5088bf80f4bSopenharmony_ci        }
5098bf80f4bSopenharmony_ci    }
5108bf80f4bSopenharmony_ci
5118bf80f4bSopenharmony_ci    bool RollOverPrefix(size_t& ix, const BASE_NS::string_view& name, const BASE_NS::string_view& prefix)
5128bf80f4bSopenharmony_ci    {
5138bf80f4bSopenharmony_ci        while (ix < name.length() && name[ix] == '/') {
5148bf80f4bSopenharmony_ci            ix++;
5158bf80f4bSopenharmony_ci        }
5168bf80f4bSopenharmony_ci
5178bf80f4bSopenharmony_ci        if (name.substr(ix).find(prefix) == 0) {
5188bf80f4bSopenharmony_ci            ix += prefix.length();
5198bf80f4bSopenharmony_ci        }
5208bf80f4bSopenharmony_ci
5218bf80f4bSopenharmony_ci        while (ix < name.length() && name[ix] == '/') {
5228bf80f4bSopenharmony_ci            ix++;
5238bf80f4bSopenharmony_ci        }
5248bf80f4bSopenharmony_ci
5258bf80f4bSopenharmony_ci        return ix < name.length();
5268bf80f4bSopenharmony_ci    }
5278bf80f4bSopenharmony_ci
5288bf80f4bSopenharmony_ci    void AddMaterial(SCENE_NS::IMaterial::Ptr material) override
5298bf80f4bSopenharmony_ci    {
5308bf80f4bSopenharmony_ci        auto materials = Materials()->GetValue();
5318bf80f4bSopenharmony_ci        auto it = std::find(materials.begin(), materials.end(), material);
5328bf80f4bSopenharmony_ci        if (it != materials.end()) {
5338bf80f4bSopenharmony_ci            // Already exists.
5348bf80f4bSopenharmony_ci            CORE_LOG_D("Trying to add same material to scene multiple times.");
5358bf80f4bSopenharmony_ci            return;
5368bf80f4bSopenharmony_ci        }
5378bf80f4bSopenharmony_ci
5388bf80f4bSopenharmony_ci        Materials()->AddValue(material);
5398bf80f4bSopenharmony_ci        UpdateCachedReference(interface_pointer_cast<SCENE_NS::INode>(material));
5408bf80f4bSopenharmony_ci    }
5418bf80f4bSopenharmony_ci
5428bf80f4bSopenharmony_ci    void RemoveMaterial(SCENE_NS::IMaterial::Ptr material) override
5438bf80f4bSopenharmony_ci    {
5448bf80f4bSopenharmony_ci        auto lock = Materials().GetLockedAccess();
5458bf80f4bSopenharmony_ci        auto vec = lock->GetValue();
5468bf80f4bSopenharmony_ci        for (size_t index = 0; index != vec.size(); ++index) {
5478bf80f4bSopenharmony_ci            if (vec[index] == material) {
5488bf80f4bSopenharmony_ci                lock->RemoveAt(index);
5498bf80f4bSopenharmony_ci                break;
5508bf80f4bSopenharmony_ci            }
5518bf80f4bSopenharmony_ci        }
5528bf80f4bSopenharmony_ci
5538bf80f4bSopenharmony_ci        for (auto&& ite : materials_) {
5548bf80f4bSopenharmony_ci            if (ite.second.lock() == material) {
5558bf80f4bSopenharmony_ci                ReleaseMaterial(ite.first);
5568bf80f4bSopenharmony_ci                break;
5578bf80f4bSopenharmony_ci            }
5588bf80f4bSopenharmony_ci        }
5598bf80f4bSopenharmony_ci    }
5608bf80f4bSopenharmony_ci
5618bf80f4bSopenharmony_ci    BASE_NS::vector<SCENE_NS::ICamera::Ptr> GetCameras() const override
5628bf80f4bSopenharmony_ci    {
5638bf80f4bSopenharmony_ci        BASE_NS::vector<SCENE_NS::ICamera::Ptr> result;
5648bf80f4bSopenharmony_ci        for (auto c : cameras_) {
5658bf80f4bSopenharmony_ci            if (auto cam = c.second.lock()) {
5668bf80f4bSopenharmony_ci                result.push_back(cam);
5678bf80f4bSopenharmony_ci            }
5688bf80f4bSopenharmony_ci        }
5698bf80f4bSopenharmony_ci        return result;
5708bf80f4bSopenharmony_ci    }
5718bf80f4bSopenharmony_ci
5728bf80f4bSopenharmony_ci    BASE_NS::vector<SCENE_NS::IMaterial::Ptr> GetMaterials() const override
5738bf80f4bSopenharmony_ci    {
5748bf80f4bSopenharmony_ci        BASE_NS::vector<SCENE_NS::IMaterial::Ptr> result;
5758bf80f4bSopenharmony_ci        for (auto& material : materials_) {
5768bf80f4bSopenharmony_ci            auto ptr = material.second.lock();
5778bf80f4bSopenharmony_ci            if (ptr) {
5788bf80f4bSopenharmony_ci                result.push_back(ptr);
5798bf80f4bSopenharmony_ci            }
5808bf80f4bSopenharmony_ci        }
5818bf80f4bSopenharmony_ci
5828bf80f4bSopenharmony_ci        return result;
5838bf80f4bSopenharmony_ci    }
5848bf80f4bSopenharmony_ci
5858bf80f4bSopenharmony_ci    // Returns a material from the scene with a given path
5868bf80f4bSopenharmony_ci    SCENE_NS::IMaterial::Ptr GetMaterial(const BASE_NS::string_view name) override
5878bf80f4bSopenharmony_ci    {
5888bf80f4bSopenharmony_ci        // The material file, aka uri-path is somewhat parallel due ownership is different for uri-materials
5898bf80f4bSopenharmony_ci        // however, one can claim traditional handle and have them preserved
5908bf80f4bSopenharmony_ci        // through flat cache. Should be consolidated someday.
5918bf80f4bSopenharmony_ci        if (name.find("://") != BASE_NS::string_view::npos) {
5928bf80f4bSopenharmony_ci            return GetOrLoadMaterial(name);
5938bf80f4bSopenharmony_ci        }
5948bf80f4bSopenharmony_ci
5958bf80f4bSopenharmony_ci        size_t ix = 0;
5968bf80f4bSopenharmony_ci        if (!RollOverPrefix(ix, name, MATERIALS_PREFIX)) {
5978bf80f4bSopenharmony_ci            return { nullptr };
5988bf80f4bSopenharmony_ci        }
5998bf80f4bSopenharmony_ci
6008bf80f4bSopenharmony_ci        // check cache (first with name:entityid)
6018bf80f4bSopenharmony_ci        if (auto it = materials_.find(name.substr(ix)); it != materials_.end()) {
6028bf80f4bSopenharmony_ci            if (auto material = it->second.lock()) {
6038bf80f4bSopenharmony_ci                return material;
6048bf80f4bSopenharmony_ci            }
6058bf80f4bSopenharmony_ci        }
6068bf80f4bSopenharmony_ci
6078bf80f4bSopenharmony_ci        // check cache (direct material name)
6088bf80f4bSopenharmony_ci        for (auto entry : materials_) {
6098bf80f4bSopenharmony_ci            auto material = entry.second.lock();
6108bf80f4bSopenharmony_ci            if (auto node = interface_pointer_cast<SCENE_NS::INode>(material)) {
6118bf80f4bSopenharmony_ci                if (node->Name()->GetValue() == name) {
6128bf80f4bSopenharmony_ci                    return material;
6138bf80f4bSopenharmony_ci                }
6148bf80f4bSopenharmony_ci            }
6158bf80f4bSopenharmony_ci        }
6168bf80f4bSopenharmony_ci
6178bf80f4bSopenharmony_ci        if (auto it = materials_.find(name.substr(ix)); it != materials_.end()) {
6188bf80f4bSopenharmony_ci            if (auto material = it->second.lock()) {
6198bf80f4bSopenharmony_ci                return material;
6208bf80f4bSopenharmony_ci            }
6218bf80f4bSopenharmony_ci        }
6228bf80f4bSopenharmony_ci
6238bf80f4bSopenharmony_ci        // Create EcsObject. When running asynchronously, we have no way of knowing if we should create
6248bf80f4bSopenharmony_ci        // new node or just rely that existing will be found
6258bf80f4bSopenharmony_ci        auto mat = interface_pointer_cast<SCENE_NS::IMaterial>(CreateNode(name.substr(ix), false,
6268bf80f4bSopenharmony_ci            SCENE_NS::ClassId::Material.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
6278bf80f4bSopenharmony_ci
6288bf80f4bSopenharmony_ci        return mat;
6298bf80f4bSopenharmony_ci    }
6308bf80f4bSopenharmony_ci
6318bf80f4bSopenharmony_ci    BASE_NS::vector<SCENE_NS::IMesh::Ptr> GetMeshes() const override
6328bf80f4bSopenharmony_ci    {
6338bf80f4bSopenharmony_ci        BASE_NS::vector<SCENE_NS::IMesh::Ptr> result;
6348bf80f4bSopenharmony_ci        for (auto mesh : meshes_) {
6358bf80f4bSopenharmony_ci            auto ptr = mesh.second.lock();
6368bf80f4bSopenharmony_ci            if (ptr) {
6378bf80f4bSopenharmony_ci                result.push_back(ptr);
6388bf80f4bSopenharmony_ci            }
6398bf80f4bSopenharmony_ci        }
6408bf80f4bSopenharmony_ci
6418bf80f4bSopenharmony_ci        return result;
6428bf80f4bSopenharmony_ci    }
6438bf80f4bSopenharmony_ci
6448bf80f4bSopenharmony_ci    // Returns a material from the scene with a given name
6458bf80f4bSopenharmony_ci    SCENE_NS::IMesh::Ptr GetMesh(const BASE_NS::string_view name) override
6468bf80f4bSopenharmony_ci    {
6478bf80f4bSopenharmony_ci        size_t ix = 0;
6488bf80f4bSopenharmony_ci        if (!RollOverPrefix(ix, name, MESHES_PREFIX)) {
6498bf80f4bSopenharmony_ci            return { nullptr };
6508bf80f4bSopenharmony_ci        }
6518bf80f4bSopenharmony_ci
6528bf80f4bSopenharmony_ci        // check cache
6538bf80f4bSopenharmony_ci        if (auto it = meshes_.find(name.substr(ix)); it != meshes_.end()) {
6548bf80f4bSopenharmony_ci            if (auto mesh = it->second.lock()) {
6558bf80f4bSopenharmony_ci                return mesh;
6568bf80f4bSopenharmony_ci            }
6578bf80f4bSopenharmony_ci        }
6588bf80f4bSopenharmony_ci        // Create EcsObject. When running asynchronously, we have no way of knowing if we should create
6598bf80f4bSopenharmony_ci        // new node or just rely that existing will be found
6608bf80f4bSopenharmony_ci        auto mesh = interface_pointer_cast<SCENE_NS::IMesh>(CreateNode(name.substr(ix), false,
6618bf80f4bSopenharmony_ci            SCENE_NS::ClassId::Mesh.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
6628bf80f4bSopenharmony_ci
6638bf80f4bSopenharmony_ci        return mesh;
6648bf80f4bSopenharmony_ci    }
6658bf80f4bSopenharmony_ci
6668bf80f4bSopenharmony_ci    BASE_NS::string constructPath(CORE_NS::Entity ent) const
6678bf80f4bSopenharmony_ci    {
6688bf80f4bSopenharmony_ci        auto ecs = GetEcs();
6698bf80f4bSopenharmony_ci        auto* nodeManager = CORE_NS::GetManager<CORE3D_NS::INodeComponentManager>(*ecs);
6708bf80f4bSopenharmony_ci        auto* nameManager = CORE_NS::GetManager<CORE3D_NS::INameComponentManager>(*ecs);
6718bf80f4bSopenharmony_ci        BASE_NS::string path;
6728bf80f4bSopenharmony_ci        if (nodeManager && nameManager) {
6738bf80f4bSopenharmony_ci            auto curent = ent;
6748bf80f4bSopenharmony_ci            for (;;) {
6758bf80f4bSopenharmony_ci                if (!CORE_NS::EntityUtil::IsValid(curent)) {
6768bf80f4bSopenharmony_ci                    // Reached root.
6778bf80f4bSopenharmony_ci                    break;
6788bf80f4bSopenharmony_ci                }
6798bf80f4bSopenharmony_ci                if (!nodeManager->HasComponent(curent)) {
6808bf80f4bSopenharmony_ci                    // not a node?
6818bf80f4bSopenharmony_ci                    return "";
6828bf80f4bSopenharmony_ci                }
6838bf80f4bSopenharmony_ci                if (!nameManager->HasComponent(curent)) {
6848bf80f4bSopenharmony_ci                    // no name in hierarchy. "fail"? or generate "name" for path..
6858bf80f4bSopenharmony_ci                    return "";
6868bf80f4bSopenharmony_ci                }
6878bf80f4bSopenharmony_ci                auto namecomp = nameManager->Get(curent);
6888bf80f4bSopenharmony_ci                if (!path.empty()) {
6898bf80f4bSopenharmony_ci                    path.insert(0, "/");
6908bf80f4bSopenharmony_ci                }
6918bf80f4bSopenharmony_ci                path.insert(0, namecomp.name.c_str());
6928bf80f4bSopenharmony_ci                const auto& node = nodeManager->Get(curent);
6938bf80f4bSopenharmony_ci                curent = node.parent;
6948bf80f4bSopenharmony_ci            }
6958bf80f4bSopenharmony_ci        }
6968bf80f4bSopenharmony_ci        if (!path.empty()) {
6978bf80f4bSopenharmony_ci            path.insert(0, "/");
6988bf80f4bSopenharmony_ci        }
6998bf80f4bSopenharmony_ci        return path;
7008bf80f4bSopenharmony_ci    }
7018bf80f4bSopenharmony_ci    // Implementation for scene change callbacks.
7028bf80f4bSopenharmony_ci    // The tricky bit here is that we may have placeholders on containers that may have received serialized data
7038bf80f4bSopenharmony_ci    // during construction, so we just cannot replace those, but need to rebind them instead
7048bf80f4bSopenharmony_ci    void onSceneInitialized(const BASE_NS::string& rootId, const BASE_NS::string& cameraId)
7058bf80f4bSopenharmony_ci    {
7068bf80f4bSopenharmony_ci        // Set root node
7078bf80f4bSopenharmony_ci        rootNodeId_ = rootId;
7088bf80f4bSopenharmony_ci        auto rootIdNormalized = NormalizePath(rootNodeId_);
7098bf80f4bSopenharmony_ci
7108bf80f4bSopenharmony_ci        if (rootNodePtr_) {
7118bf80f4bSopenharmony_ci            // check if someone assigned us a root node while we are preserving the previous one
7128bf80f4bSopenharmony_ci            if (!META_ACCESS_PROPERTY(RootNode)->GetValue()) {
7138bf80f4bSopenharmony_ci                META_ACCESS_PROPERTY(RootNode)->SetValue(rootNodePtr_);
7148bf80f4bSopenharmony_ci            }
7158bf80f4bSopenharmony_ci            rootNodePtr_.reset();
7168bf80f4bSopenharmony_ci        }
7178bf80f4bSopenharmony_ci
7188bf80f4bSopenharmony_ci        if (cameraNodePtr_) {
7198bf80f4bSopenharmony_ci            // check if someone assigned us a root node while we are preserving the previous one
7208bf80f4bSopenharmony_ci            if (!META_ACCESS_PROPERTY(DefaultCamera)->GetValue()) {
7218bf80f4bSopenharmony_ci                META_ACCESS_PROPERTY(DefaultCamera)->SetValue(cameraNodePtr_);
7228bf80f4bSopenharmony_ci            }
7238bf80f4bSopenharmony_ci            cameraNodePtr_.reset();
7248bf80f4bSopenharmony_ci        }
7258bf80f4bSopenharmony_ci
7268bf80f4bSopenharmony_ci        if (auto renderConfiguration = META_NS::GetValue(RenderConfiguration())) {
7278bf80f4bSopenharmony_ci            // Ensure the render configuration is bound to scene.
7288bf80f4bSopenharmony_ci            auto resource = interface_pointer_cast<IResourcePrivate>(renderConfiguration);
7298bf80f4bSopenharmony_ci            if (resource) {
7308bf80f4bSopenharmony_ci                resource->Connect(sceneHolder_);
7318bf80f4bSopenharmony_ci            }
7328bf80f4bSopenharmony_ci        }
7338bf80f4bSopenharmony_ci
7348bf80f4bSopenharmony_ci        // setup subscription for toolkit changes
7358bf80f4bSopenharmony_ci        RenderConfiguration()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
7368bf80f4bSopenharmony_ci            auto resource = interface_pointer_cast<IResourcePrivate>(GetValue(RenderConfiguration()));
7378bf80f4bSopenharmony_ci            if (resource) {
7388bf80f4bSopenharmony_ci                resource->Connect(sceneHolder_);
7398bf80f4bSopenharmony_ci            }
7408bf80f4bSopenharmony_ci        }),
7418bf80f4bSopenharmony_ci            reinterpret_cast<uint64_t>(this));
7428bf80f4bSopenharmony_ci
7438bf80f4bSopenharmony_ci        if (auto rootNode = META_ACCESS_PROPERTY(RootNode)->GetValue()) {
7448bf80f4bSopenharmony_ci            // switch new ecsObject
7458bf80f4bSopenharmony_ci            BindNodeToEcs(rootNode, rootIdNormalized, false);
7468bf80f4bSopenharmony_ci            nodes_[rootIdNormalized] = rootNode;
7478bf80f4bSopenharmony_ci        } else {
7488bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(RootNode)->SetValue(GetSelf<SCENE_NS::IScene>()->GetNode<SCENE_NS::INode>(rootId));
7498bf80f4bSopenharmony_ci        }
7508bf80f4bSopenharmony_ci
7518bf80f4bSopenharmony_ci        auto defaultCamera = META_ACCESS_PROPERTY(DefaultCamera)->GetValue();
7528bf80f4bSopenharmony_ci        if (!defaultCamera) {
7538bf80f4bSopenharmony_ci            defaultCamera = GetSelf<SCENE_NS::IScene>()->GetNode<SCENE_NS::ICamera>(cameraId);
7548bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(DefaultCamera)->SetValue(defaultCamera);
7558bf80f4bSopenharmony_ci        }
7568bf80f4bSopenharmony_ci
7578bf80f4bSopenharmony_ci        if (defaultCamera) {
7588bf80f4bSopenharmony_ci            ActivateCamera(defaultCamera);
7598bf80f4bSopenharmony_ci        }
7608bf80f4bSopenharmony_ci
7618bf80f4bSopenharmony_ci        // under normal conditions, this would take place asyncronously
7628bf80f4bSopenharmony_ci        // however, if we are running on synchronous mode we need to restore the ecs bindings here
7638bf80f4bSopenharmony_ci        for (auto& prevNode : nodes_) {
7648bf80f4bSopenharmony_ci            if (prevNode.first != rootIdNormalized && prevNode.first != cameraId) {
7658bf80f4bSopenharmony_ci                BindNodeToEcs(prevNode.second, prevNode.first, false);
7668bf80f4bSopenharmony_ci            }
7678bf80f4bSopenharmony_ci        }
7688bf80f4bSopenharmony_ci
7698bf80f4bSopenharmony_ci        // This is information only unless we instantiate all the scene nodes immediately
7708bf80f4bSopenharmony_ci        // Should be flagged more effectively on production builds
7718bf80f4bSopenharmony_ci        CORE3D_NS::INodeSystem& nodeSystem = *CORE_NS::GetSystem<CORE3D_NS::INodeSystem>(*GetEcs());
7728bf80f4bSopenharmony_ci        const auto& root = nodeSystem.GetRootNode();
7738bf80f4bSopenharmony_ci
7748bf80f4bSopenharmony_ci        instantiateNodes(root, "/");
7758bf80f4bSopenharmony_ci
7768bf80f4bSopenharmony_ci        // notify observers about the change
7778bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Status)->SetValue(SCENE_NS::IScene::SCENE_STATUS_READY);
7788bf80f4bSopenharmony_ci        META_NS::Invoke<META_NS::IOnChanged>(OnLoaded());
7798bf80f4bSopenharmony_ci
7808bf80f4bSopenharmony_ci        sceneHolder_->SetUninitializeCallback(
7818bf80f4bSopenharmony_ci            META_NS::MakeCallback<SceneHolder::ISceneUninitialized>([me = BASE_NS::weak_ptr(GetSelf())]() {
7828bf80f4bSopenharmony_ci                if (auto self = me.lock().get())
7838bf80f4bSopenharmony_ci                    static_cast<SceneImpl*>(self)->DetachScene();
7848bf80f4bSopenharmony_ci            }),
7858bf80f4bSopenharmony_ci            sceneHolder_);
7868bf80f4bSopenharmony_ci    }
7878bf80f4bSopenharmony_ci
7888bf80f4bSopenharmony_ci    void OnSceneLoaded(uint32_t status)
7898bf80f4bSopenharmony_ci    {
7908bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Status)->SetValue(status);
7918bf80f4bSopenharmony_ci        if (status == SCENE_NS::IScene::SCENE_STATUS_READY) {
7928bf80f4bSopenharmony_ci            META_NS::Invoke<META_NS::IOnChanged>(OnLoaded());
7938bf80f4bSopenharmony_ci        }
7948bf80f4bSopenharmony_ci    }
7958bf80f4bSopenharmony_ci
7968bf80f4bSopenharmony_ci    // Implementation for scene processing.
7978bf80f4bSopenharmony_ci
7988bf80f4bSopenharmony_ci    void DetachScene(bool setDirty = false)
7998bf80f4bSopenharmony_ci    {
8008bf80f4bSopenharmony_ci        if (META_ACCESS_PROPERTY(Status)) {
8018bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_UNINITIALIZED);
8028bf80f4bSopenharmony_ci        }
8038bf80f4bSopenharmony_ci
8048bf80f4bSopenharmony_ci        cameras_.clear();
8058bf80f4bSopenharmony_ci        materials_.clear();
8068bf80f4bSopenharmony_ci        meshes_.clear();
8078bf80f4bSopenharmony_ci        animations_.clear();
8088bf80f4bSopenharmony_ci        nodes_.clear();
8098bf80f4bSopenharmony_ci
8108bf80f4bSopenharmony_ci        // This effectively means that we keep the scene user objects forcibly live
8118bf80f4bSopenharmony_ci        // until we get fresh ones to replace them, not sure if that is intended
8128bf80f4bSopenharmony_ci        rootNodePtr_ = META_ACCESS_PROPERTY(RootNode)->GetValue();
8138bf80f4bSopenharmony_ci        cameraNodePtr_ = META_ACCESS_PROPERTY(DefaultCamera)->GetValue();
8148bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(RootNode)->SetValue({});
8158bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(DefaultCamera)->SetValue({});
8168bf80f4bSopenharmony_ci
8178bf80f4bSopenharmony_ci        if (setDirty) {
8188bf80f4bSopenharmony_ci            for (const auto& bitmap : bitmaps_) {
8198bf80f4bSopenharmony_ci                /*auto externalBitmap = interface_pointer_cast<UI_NS::ILume2DExternalBitmap>(bitmap.second.bitmap);
8208bf80f4bSopenharmony_ci                if (externalBitmap) {
8218bf80f4bSopenharmony_ci                    externalBitmap->SetCoreBitmap(nullptr);
8228bf80f4bSopenharmony_ci                }*/
8238bf80f4bSopenharmony_ci                bitmap.second.bitmap->SetRenderHandle({}, { 0, 0 });
8248bf80f4bSopenharmony_ci            }
8258bf80f4bSopenharmony_ci        }
8268bf80f4bSopenharmony_ci    }
8278bf80f4bSopenharmony_ci
8288bf80f4bSopenharmony_ci    void instantiateNodes(const CORE3D_NS::ISceneNode& node, BASE_NS::string path)
8298bf80f4bSopenharmony_ci    {
8308bf80f4bSopenharmony_ci        if (!path.empty() && path.back() != '/') {
8318bf80f4bSopenharmony_ci            path.append("/");
8328bf80f4bSopenharmony_ci        }
8338bf80f4bSopenharmony_ci        if (!node.GetName().empty()) {
8348bf80f4bSopenharmony_ci            path.append(node.GetName());
8358bf80f4bSopenharmony_ci        } else {
8368bf80f4bSopenharmony_ci#ifndef INSTANTIATE_NODES_ON_INITIALIZE
8378bf80f4bSopenharmony_ci            path.append("[");
8388bf80f4bSopenharmony_ci            path.append(BASE_NS::to_string(node.GetEntity().id));
8398bf80f4bSopenharmony_ci            path.append("]");
8408bf80f4bSopenharmony_ci#endif
8418bf80f4bSopenharmony_ci        }
8428bf80f4bSopenharmony_ci        SCENE_PLUGIN_VERBOSE_LOG("%s", path.c_str());
8438bf80f4bSopenharmony_ci#ifdef INSTANTIATE_NODES_ON_INITIALIZE
8448bf80f4bSopenharmony_ci        GetNode(path);
8458bf80f4bSopenharmony_ci#endif
8468bf80f4bSopenharmony_ci
8478bf80f4bSopenharmony_ci        for (const auto child : node.GetChildren())
8488bf80f4bSopenharmony_ci            instantiateNodes(*child, path);
8498bf80f4bSopenharmony_ci    }
8508bf80f4bSopenharmony_ci
8518bf80f4bSopenharmony_ci    BASE_NS::string NormalizePath(const BASE_NS::string_view& path)
8528bf80f4bSopenharmony_ci    {
8538bf80f4bSopenharmony_ci        BASE_NS::string patchedPath;
8548bf80f4bSopenharmony_ci        if (path == rootNodeId_) {
8558bf80f4bSopenharmony_ci            patchedPath = path;
8568bf80f4bSopenharmony_ci            patchedPath.insert(0, "/");
8578bf80f4bSopenharmony_ci        } else {
8588bf80f4bSopenharmony_ci            bool hasSlash = (path[0] == '/');
8598bf80f4bSopenharmony_ci            if (path.compare(hasSlash ? 1 : 0, rootNodeId_.size(), rootNodeId_.data())) {
8608bf80f4bSopenharmony_ci                SCENE_PLUGIN_VERBOSE_LOG("%s: the path does not contain root node, patching the path", __func__);
8618bf80f4bSopenharmony_ci                patchedPath.append("/");
8628bf80f4bSopenharmony_ci                patchedPath.append(rootNodeId_);
8638bf80f4bSopenharmony_ci                if (!hasSlash) {
8648bf80f4bSopenharmony_ci                    patchedPath.append("/");
8658bf80f4bSopenharmony_ci                }
8668bf80f4bSopenharmony_ci                patchedPath.append(path.data(), path.size());
8678bf80f4bSopenharmony_ci            } else {
8688bf80f4bSopenharmony_ci                patchedPath = path;
8698bf80f4bSopenharmony_ci                if (!hasSlash) {
8708bf80f4bSopenharmony_ci                    patchedPath = "/" + patchedPath;
8718bf80f4bSopenharmony_ci                }
8728bf80f4bSopenharmony_ci            }
8738bf80f4bSopenharmony_ci        }
8748bf80f4bSopenharmony_ci        return patchedPath;
8758bf80f4bSopenharmony_ci    }
8768bf80f4bSopenharmony_ci
8778bf80f4bSopenharmony_ci    SCENE_NS::IEcsObject::Ptr FindEcsObject(const BASE_NS::string_view& path, BASE_NS::string& patchedPath)
8788bf80f4bSopenharmony_ci    {
8798bf80f4bSopenharmony_ci        patchedPath = NormalizePath(path);
8808bf80f4bSopenharmony_ci
8818bf80f4bSopenharmony_ci        if (auto it = nodes_.find(patchedPath); it != nodes_.end()) {
8828bf80f4bSopenharmony_ci            return interface_pointer_cast<SCENE_NS::IEcsObject>(it->second);
8838bf80f4bSopenharmony_ci        }
8848bf80f4bSopenharmony_ci        return SCENE_NS::IEcsObject::Ptr {};
8858bf80f4bSopenharmony_ci    }
8868bf80f4bSopenharmony_ci
8878bf80f4bSopenharmony_ci    SCENE_NS::IEcsObject::Ptr CreateNewEcsObject(const BASE_NS::string& /*path*/)
8888bf80f4bSopenharmony_ci    {
8898bf80f4bSopenharmony_ci        // Create new helper object
8908bf80f4bSopenharmony_ci        // Not much left here, could be presumably removed
8918bf80f4bSopenharmony_ci        auto ret = SCENE_NS::IEcsObject::Ptr {};
8928bf80f4bSopenharmony_ci
8938bf80f4bSopenharmony_ci// Allow construction to progress even ecs was not available,
8948bf80f4bSopenharmony_ci// nodes will perform initialization asynchronously anyway
8958bf80f4bSopenharmony_ci        if (auto object = META_NS::GetObjectRegistry().Create(SCENE_NS::ClassId::EcsObject)) {
8968bf80f4bSopenharmony_ci            ret = interface_pointer_cast<SCENE_NS::IEcsObject>(object);
8978bf80f4bSopenharmony_ci        }
8988bf80f4bSopenharmony_ci        return ret;
8998bf80f4bSopenharmony_ci    }
9008bf80f4bSopenharmony_ci
9018bf80f4bSopenharmony_ci    SCENE_NS::IEcsObject::Ptr GetEcsObject(const BASE_NS::string_view& path) override
9028bf80f4bSopenharmony_ci    {
9038bf80f4bSopenharmony_ci        return interface_pointer_cast<SCENE_NS::IEcsObject>(
9048bf80f4bSopenharmony_ci            GetNode(path, META_NS::IObject::UID, SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
9058bf80f4bSopenharmony_ci    }
9068bf80f4bSopenharmony_ci
9078bf80f4bSopenharmony_ci    CORE_NS::IEcs::Ptr GetEcs() override
9088bf80f4bSopenharmony_ci    {
9098bf80f4bSopenharmony_ci        return sceneHolder_->GetEcs();
9108bf80f4bSopenharmony_ci    }
9118bf80f4bSopenharmony_ci
9128bf80f4bSopenharmony_ci    CORE_NS::IEcs::Ptr GetEcs() const
9138bf80f4bSopenharmony_ci    {
9148bf80f4bSopenharmony_ci        return sceneHolder_->GetEcs();
9158bf80f4bSopenharmony_ci    }
9168bf80f4bSopenharmony_ci
9178bf80f4bSopenharmony_ci    META_NS::ITaskQueue::Token AddEngineTask(
9188bf80f4bSopenharmony_ci        const META_NS::ITaskQueue::CallableType::Ptr& task, bool runDeferred) override
9198bf80f4bSopenharmony_ci    {
9208bf80f4bSopenharmony_ci        if (sceneHolder_) {
9218bf80f4bSopenharmony_ci            return sceneHolder_->QueueEngineTask(task, runDeferred);
9228bf80f4bSopenharmony_ci        }
9238bf80f4bSopenharmony_ci        return META_NS::ITaskQueue::Token {};
9248bf80f4bSopenharmony_ci    }
9258bf80f4bSopenharmony_ci
9268bf80f4bSopenharmony_ci    META_NS::ITaskQueue::Token AddApplicationTask(
9278bf80f4bSopenharmony_ci        const META_NS::ITaskQueue::CallableType::Ptr& task, bool runDeferred) override
9288bf80f4bSopenharmony_ci    {
9298bf80f4bSopenharmony_ci        if (sceneHolder_) {
9308bf80f4bSopenharmony_ci            return sceneHolder_->QueueApplicationTask(task, runDeferred);
9318bf80f4bSopenharmony_ci        }
9328bf80f4bSopenharmony_ci        return META_NS::ITaskQueue::Token {};
9338bf80f4bSopenharmony_ci    }
9348bf80f4bSopenharmony_ci
9358bf80f4bSopenharmony_ci    void CancelEngineTask(META_NS::ITaskQueue::Token token) override
9368bf80f4bSopenharmony_ci    {
9378bf80f4bSopenharmony_ci        if (sceneHolder_) {
9388bf80f4bSopenharmony_ci            return sceneHolder_->CancelEngineTask(token);
9398bf80f4bSopenharmony_ci        }
9408bf80f4bSopenharmony_ci    }
9418bf80f4bSopenharmony_ci
9428bf80f4bSopenharmony_ci    void CancelAppTask(META_NS::ITaskQueue::Token token) override
9438bf80f4bSopenharmony_ci    {
9448bf80f4bSopenharmony_ci        if (sceneHolder_) {
9458bf80f4bSopenharmony_ci            return sceneHolder_->CancelAppTask(token);
9468bf80f4bSopenharmony_ci        }
9478bf80f4bSopenharmony_ci    }
9488bf80f4bSopenharmony_ci
9498bf80f4bSopenharmony_ci    SCENE_NS::IEntityCollection* GetEntityCollection() override
9508bf80f4bSopenharmony_ci    {
9518bf80f4bSopenharmony_ci        if (sceneHolder_) {
9528bf80f4bSopenharmony_ci            return sceneHolder_->GetEntityCollection();
9538bf80f4bSopenharmony_ci        }
9548bf80f4bSopenharmony_ci
9558bf80f4bSopenharmony_ci        return {};
9568bf80f4bSopenharmony_ci    }
9578bf80f4bSopenharmony_ci
9588bf80f4bSopenharmony_ci    SCENE_NS::IAssetManager* GetAssetManager() override
9598bf80f4bSopenharmony_ci    {
9608bf80f4bSopenharmony_ci        if (sceneHolder_) {
9618bf80f4bSopenharmony_ci            return sceneHolder_->GetAssetManager();
9628bf80f4bSopenharmony_ci        }
9638bf80f4bSopenharmony_ci
9648bf80f4bSopenharmony_ci        return {};
9658bf80f4bSopenharmony_ci    }
9668bf80f4bSopenharmony_ci
9678bf80f4bSopenharmony_ci    SCENE_NS::IMaterial::Ptr GetOrLoadMaterial(BASE_NS::string_view uri)
9688bf80f4bSopenharmony_ci    {
9698bf80f4bSopenharmony_ci        // check cache
9708bf80f4bSopenharmony_ci        if (auto it = uriMaterials_.find(uri); it != uriMaterials_.end()) {
9718bf80f4bSopenharmony_ci            if (auto material = it->second.lock()) {
9728bf80f4bSopenharmony_ci                return material;
9738bf80f4bSopenharmony_ci            }
9748bf80f4bSopenharmony_ci        }
9758bf80f4bSopenharmony_ci
9768bf80f4bSopenharmony_ci        auto ret = LoadMaterial(uri);
9778bf80f4bSopenharmony_ci        uriMaterials_[uri] = ret;
9788bf80f4bSopenharmony_ci        return ret;
9798bf80f4bSopenharmony_ci    }
9808bf80f4bSopenharmony_ci
9818bf80f4bSopenharmony_ci    SCENE_NS::IMaterial::Ptr LoadMaterial(BASE_NS::string_view uri) override
9828bf80f4bSopenharmony_ci    {
9838bf80f4bSopenharmony_ci        bool isGltfUri = (uri.find(".glb/materials/") != BASE_NS::string_view::npos) ||
9848bf80f4bSopenharmony_ci                         (uri.find(".gltf/materials/") != BASE_NS::string_view::npos);
9858bf80f4bSopenharmony_ci        if (isGltfUri) {
9868bf80f4bSopenharmony_ci            auto resource = CreateResourceFromUri(SCENE_NS::ClassId::Material, uri);
9878bf80f4bSopenharmony_ci            if (resource) {
9888bf80f4bSopenharmony_ci                return interface_pointer_cast<SCENE_NS::IMaterial>(resource);
9898bf80f4bSopenharmony_ci            }
9908bf80f4bSopenharmony_ci
9918bf80f4bSopenharmony_ci            CORE_LOG_W("Could not resolve material URI: %s", BASE_NS::string(uri.data(), uri.size()).c_str());
9928bf80f4bSopenharmony_ci            // we could return the control to CreateMaterial and let the engine resolve the materials with uri
9938bf80f4bSopenharmony_ci            // componenta as they seem to be in place when loaded from glb or gltf
9948bf80f4bSopenharmony_ci        }
9958bf80f4bSopenharmony_ci
9968bf80f4bSopenharmony_ci        SCENE_NS::IMaterial::Ptr ret;
9978bf80f4bSopenharmony_ci
9988bf80f4bSopenharmony_ci        return ret;
9998bf80f4bSopenharmony_ci    }
10008bf80f4bSopenharmony_ci
10018bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr CreateNode(const META_NS::ObjectId classId)
10028bf80f4bSopenharmony_ci    {
10038bf80f4bSopenharmony_ci        SCENE_NS::INode::Ptr node;
10048bf80f4bSopenharmony_ci
10058bf80f4bSopenharmony_ci        if (classId == SCENE_NS::ClassId::Light.Id() || classId == SCENE_NS::ILight::UID) {
10068bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Light);
10078bf80f4bSopenharmony_ci        } else if (classId == SCENE_NS::ClassId::Camera.Id() || classId == SCENE_NS::ICamera::UID) {
10088bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Camera);
10098bf80f4bSopenharmony_ci        } /*else if (classId == SCENE_NS::ClassId::ViewNode.Id() || classId == SCENE_NS::IViewNode::UID) {
10108bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::ViewNode);
10118bf80f4bSopenharmony_ci        } */
10128bf80f4bSopenharmony_ci        else if (classId == SCENE_NS::ClassId::Material.Id() || classId == SCENE_NS::IMaterial::UID) {
10138bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Material);
10148bf80f4bSopenharmony_ci        } else if (classId == SCENE_NS::ClassId::Mesh.Id() || classId == SCENE_NS::IMesh::UID) {
10158bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Mesh);
10168bf80f4bSopenharmony_ci        } else if (classId == SCENE_NS::ClassId::Animation.Id() || classId == META_NS::IAnimation::UID) {
10178bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Animation);
10188bf80f4bSopenharmony_ci        } else if (classId == SCENE_NS::ClassId::Environment.Id() || classId == SCENE_NS::IEnvironment::UID) {
10198bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Environment);
10208bf80f4bSopenharmony_ci        } else {
10218bf80f4bSopenharmony_ci            if (classId != META_NS::IObject::UID && classId != SCENE_NS::ClassId::Node.Id() &&
10228bf80f4bSopenharmony_ci                classId != SCENE_NS::INode::UID) {
10238bf80f4bSopenharmony_ci                CORE_LOG_W("%s: uid not known, returning INode instance", __func__);
10248bf80f4bSopenharmony_ci            }
10258bf80f4bSopenharmony_ci
10268bf80f4bSopenharmony_ci            node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Node);
10278bf80f4bSopenharmony_ci        }
10288bf80f4bSopenharmony_ci
10298bf80f4bSopenharmony_ci        return node;
10308bf80f4bSopenharmony_ci    }
10318bf80f4bSopenharmony_ci
10328bf80f4bSopenharmony_ci    // Quirk / constraints:
10338bf80f4bSopenharmony_ci    // 1) has name -> cached and asynchronously initialized
10348bf80f4bSopenharmony_ci    // 2) name empty, just an object, not sure if needed at the end
10358bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr CreateNode(const BASE_NS::string_view name, bool createEngineObject,
10368bf80f4bSopenharmony_ci        const META_NS::ObjectId classId, SCENE_NS::INode::BuildBehavior buildBehavior) override
10378bf80f4bSopenharmony_ci    {
10388bf80f4bSopenharmony_ci        if (const auto& ite = nodes_.find(name); ite != nodes_.cend() && createEngineObject) {
10398bf80f4bSopenharmony_ci            CORE_LOG_W("Refusing to create new duplicate node: %s", BASE_NS::string(name.data(), name.size()).c_str());
10408bf80f4bSopenharmony_ci            return ite->second;
10418bf80f4bSopenharmony_ci        }
10428bf80f4bSopenharmony_ci
10438bf80f4bSopenharmony_ci        SCENE_NS::INode::Ptr node = CreateNode(classId);
10448bf80f4bSopenharmony_ci
10458bf80f4bSopenharmony_ci        if (node) {
10468bf80f4bSopenharmony_ci            node->BuildChildren(buildBehavior);
10478bf80f4bSopenharmony_ci            BindNodeToEcs(node, name, createEngineObject);
10488bf80f4bSopenharmony_ci        }
10498bf80f4bSopenharmony_ci        return node;
10508bf80f4bSopenharmony_ci    }
10518bf80f4bSopenharmony_ci
10528bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr CreateResourceFromUri(const META_NS::ObjectId classId, BASE_NS::string_view uri)
10538bf80f4bSopenharmony_ci    {
10548bf80f4bSopenharmony_ci        SCENE_NS::INode::Ptr node;
10558bf80f4bSopenharmony_ci
10568bf80f4bSopenharmony_ci        auto entity = sceneHolder_->GetEntityByUri(uri);
10578bf80f4bSopenharmony_ci
10588bf80f4bSopenharmony_ci        if (CORE_NS::EntityUtil::IsValid(entity)) {
10598bf80f4bSopenharmony_ci            BASE_NS::string name;
10608bf80f4bSopenharmony_ci            if (sceneHolder_->GetEntityName(entity, name)) {
10618bf80f4bSopenharmony_ci                node = CreateNode(classId);
10628bf80f4bSopenharmony_ci                if (node) {
10638bf80f4bSopenharmony_ci                    auto ecsScene = GetSelf<SCENE_NS::IEcsScene>();
10648bf80f4bSopenharmony_ci                    auto ecsObject = CreateNewEcsObject({});
10658bf80f4bSopenharmony_ci                    auto nodeInterface = interface_pointer_cast<INodeEcsInterfacePrivate>(node);
10668bf80f4bSopenharmony_ci                    nodeInterface->Initialize(ecsScene, ecsObject, {}, "", name, sceneHolder_, entity);
10678bf80f4bSopenharmony_ci                }
10688bf80f4bSopenharmony_ci            }
10698bf80f4bSopenharmony_ci        }
10708bf80f4bSopenharmony_ci
10718bf80f4bSopenharmony_ci        return node;
10728bf80f4bSopenharmony_ci    }
10738bf80f4bSopenharmony_ci
10748bf80f4bSopenharmony_ci    void BindNodeToEcs(
10758bf80f4bSopenharmony_ci        SCENE_NS::INode::Ptr& node, const BASE_NS::string_view fullPath, bool createEngineObject) override
10768bf80f4bSopenharmony_ci    {
10778bf80f4bSopenharmony_ci        SCENE_PLUGIN_VERBOSE_LOG("Scene::BindNodeToEcs called for %s", BASE_NS::string(fullPath).c_str());
10788bf80f4bSopenharmony_ci
10798bf80f4bSopenharmony_ci        CORE_NS::Entity entity;
10808bf80f4bSopenharmony_ci        auto ecsScene = GetSelf<SCENE_NS::IEcsScene>();
10818bf80f4bSopenharmony_ci
10828bf80f4bSopenharmony_ci        bool addToRootContainer { false };
10838bf80f4bSopenharmony_ci        BASE_NS::string nodePath;
10848bf80f4bSopenharmony_ci        BASE_NS::string nodeName;
10858bf80f4bSopenharmony_ci
10868bf80f4bSopenharmony_ci        auto classUid = interface_cast<META_NS::IObject>(node)->GetClassId();
10878bf80f4bSopenharmony_ci        bool isResourceClassType = (classUid == SCENE_NS::ClassId::Material) || // Material
10888bf80f4bSopenharmony_ci                                   (classUid == SCENE_NS::ClassId::Mesh) ||     // Mesh
10898bf80f4bSopenharmony_ci                                   (classUid == SCENE_NS::ClassId::Animation);  // Animation
10908bf80f4bSopenharmony_ci
10918bf80f4bSopenharmony_ci        auto isRealUri = fullPath.find("://") != BASE_NS::string_view::npos;
10928bf80f4bSopenharmony_ci
10938bf80f4bSopenharmony_ci        if (!isRealUri) {
10948bf80f4bSopenharmony_ci            auto cutIx = fullPath.find_last_of('/');
10958bf80f4bSopenharmony_ci            if (cutIx != BASE_NS::string_view::npos && cutIx < fullPath.size()) {
10968bf80f4bSopenharmony_ci                ++cutIx;
10978bf80f4bSopenharmony_ci                nodePath = BASE_NS::string(fullPath.data(), cutIx);
10988bf80f4bSopenharmony_ci                nodeName = BASE_NS::string(fullPath.data() + cutIx, fullPath.size() - cutIx);
10998bf80f4bSopenharmony_ci            } else if (!fullPath.empty()) {
11008bf80f4bSopenharmony_ci                nodeName = BASE_NS::string(fullPath.data(), fullPath.size());
11018bf80f4bSopenharmony_ci            }
11028bf80f4bSopenharmony_ci        }
11038bf80f4bSopenharmony_ci
11048bf80f4bSopenharmony_ci        if (nodeName.empty()) {
11058bf80f4bSopenharmony_ci            if (createEngineObject) {
11068bf80f4bSopenharmony_ci                nodeName = interface_cast<META_NS::IObjectInstance>(node)->GetInstanceId().ToString();
11078bf80f4bSopenharmony_ci            } else {
11088bf80f4bSopenharmony_ci                if (isRealUri) {
11098bf80f4bSopenharmony_ci                    auto ecsObject = CreateNewEcsObject({});
11108bf80f4bSopenharmony_ci                    auto nodeInterface = interface_cast<INodeEcsInterfacePrivate>(node);
11118bf80f4bSopenharmony_ci                    nodeInterface->Initialize(ecsScene, ecsObject, {}, "", BASE_NS::string(fullPath), sceneHolder_, {});
11128bf80f4bSopenharmony_ci                } else {
11138bf80f4bSopenharmony_ci                    CORE_LOG_W("%s: refusing to create proxy object without valid target, name is empty.", __func__);
11148bf80f4bSopenharmony_ci                }
11158bf80f4bSopenharmony_ci                return;
11168bf80f4bSopenharmony_ci            }
11178bf80f4bSopenharmony_ci        }
11188bf80f4bSopenharmony_ci
11198bf80f4bSopenharmony_ci        auto nodeInterface = interface_cast<INodeEcsInterfacePrivate>(node);
11208bf80f4bSopenharmony_ci
11218bf80f4bSopenharmony_ci        if (nodeInterface->EcsObject() && CORE_NS::EntityUtil::IsValid(nodeInterface->EcsObject()->GetEntity())) {
11228bf80f4bSopenharmony_ci            CORE_LOG_W("%s: refusing to recreate proxy object while current one is valid: %s", __func__,
11238bf80f4bSopenharmony_ci                BASE_NS::string(fullPath).c_str());
11248bf80f4bSopenharmony_ci            return;
11258bf80f4bSopenharmony_ci        }
11268bf80f4bSopenharmony_ci
11278bf80f4bSopenharmony_ci        auto ecsObject = CreateNewEcsObject({});
11288bf80f4bSopenharmony_ci
11298bf80f4bSopenharmony_ci            // NEW NODE
11308bf80f4bSopenharmony_ci        if (createEngineObject) {
11318bf80f4bSopenharmony_ci            auto classUid = interface_pointer_cast<META_NS::IObject>(node)->GetClassId();
11328bf80f4bSopenharmony_ci            auto constructNode = [this, nodePath, nodeName, classUid](
11338bf80f4bSopenharmony_ci                                         const auto& sceneHolder) -> CORE_NS::Entity {
11348bf80f4bSopenharmony_ci                    CORE_NS::Entity entity;
11358bf80f4bSopenharmony_ci                if (sceneHolder) {
11368bf80f4bSopenharmony_ci                    // Should not need to type this deep here
11378bf80f4bSopenharmony_ci                    if (classUid == SCENE_NS::ClassId::Material) {
11388bf80f4bSopenharmony_ci                        entity = sceneHolder->CreateMaterial(nodeName);
11398bf80f4bSopenharmony_ci                    } else if (classUid == SCENE_NS::ClassId::Camera) {
11408bf80f4bSopenharmony_ci                        entity = sceneHolder->CreateCamera(nodePath, nodeName, 0);
11418bf80f4bSopenharmony_ci                    } else if (auto node = sceneHolder->CreateNode(nodePath, nodeName)) {
11428bf80f4bSopenharmony_ci                        entity = node->GetEntity();
11438bf80f4bSopenharmony_ci
11448bf80f4bSopenharmony_ci                        sceneHolder->EnableLayerComponent(entity);
11458bf80f4bSopenharmony_ci                        if (classUid == SCENE_NS::ClassId::Light) {
11468bf80f4bSopenharmony_ci                            sceneHolder->EnableLightComponent(entity);
11478bf80f4bSopenharmony_ci                        }
11488bf80f4bSopenharmony_ci                        if (classUid == SCENE_NS::ClassId::Environment) {
11498bf80f4bSopenharmony_ci                            sceneHolder->EnableEnvironmentComponent(entity);
11508bf80f4bSopenharmony_ci                        }
11518bf80f4bSopenharmony_ci                    }
11528bf80f4bSopenharmony_ci                }
11538bf80f4bSopenharmony_ci
11548bf80f4bSopenharmony_ci                return entity;
11558bf80f4bSopenharmony_ci            };
11568bf80f4bSopenharmony_ci            if (GetValue(Asynchronous())) {
11578bf80f4bSopenharmony_ci                AddEngineTask(MakeTask(
11588bf80f4bSopenharmony_ci                                  [constructNode](const auto& sceneHolder) {
11598bf80f4bSopenharmony_ci                                      constructNode(sceneHolder);
11608bf80f4bSopenharmony_ci                                      return false;
11618bf80f4bSopenharmony_ci                                  },
11628bf80f4bSopenharmony_ci                                  sceneHolder_),
11638bf80f4bSopenharmony_ci                    false);
11648bf80f4bSopenharmony_ci            } else {
11658bf80f4bSopenharmony_ci                entity = constructNode(sceneHolder_);
11668bf80f4bSopenharmony_ci            }
11678bf80f4bSopenharmony_ci
11688bf80f4bSopenharmony_ci            addToRootContainer = true;
11698bf80f4bSopenharmony_ci            // If we don't have parent .. then attach to root.
11708bf80f4bSopenharmony_ci            if (nodePath.empty() && !isResourceClassType) {
11718bf80f4bSopenharmony_ci                nodePath = "/" + rootNodeId_ + "/";
11728bf80f4bSopenharmony_ci            }
11738bf80f4bSopenharmony_ci
11748bf80f4bSopenharmony_ci            if (META_NS::Property<uint32_t> creationPolicy = nodeInterface->GetLifecycleInfo()) {
11758bf80f4bSopenharmony_ci                creationPolicy->SetValue(NODE_LC_CREATED);
11768bf80f4bSopenharmony_ci                nodeInterface->ClaimOwnershipOfEntity(true);
11778bf80f4bSopenharmony_ci            }
11788bf80f4bSopenharmony_ci        } else {
11798bf80f4bSopenharmony_ci            // We expect to find the node from ecs (sooner or later)
11808bf80f4bSopenharmony_ci            // If we are running synchronously, we could check it and even
11818bf80f4bSopenharmony_ci            // tell the calling code if the node is there.
11828bf80f4bSopenharmony_ci
11838bf80f4bSopenharmony_ci            // We'd need to know some additional info about the node parent etc
11848bf80f4bSopenharmony_ci            if (META_NS::Property<uint32_t> creationPolicy = nodeInterface->GetLifecycleInfo()) {
11858bf80f4bSopenharmony_ci                creationPolicy->SetValue(NODE_LC_MIRROR_EXISTING);
11868bf80f4bSopenharmony_ci            }
11878bf80f4bSopenharmony_ci        }
11888bf80f4bSopenharmony_ci
11898bf80f4bSopenharmony_ci        SCENE_NS::INode::Ptr parent;
11908bf80f4bSopenharmony_ci
11918bf80f4bSopenharmony_ci        if (!isResourceClassType) {
11928bf80f4bSopenharmony_ci            auto containable = interface_cast<META_NS::IContainable>(node);
11938bf80f4bSopenharmony_ci            if (containable->GetParent()) {
11948bf80f4bSopenharmony_ci                parent = interface_pointer_cast<SCENE_NS::INode>(containable->GetParent());
11958bf80f4bSopenharmony_ci            }
11968bf80f4bSopenharmony_ci
11978bf80f4bSopenharmony_ci            if (!parent) {
11988bf80f4bSopenharmony_ci                parent = addToRootContainer ? RootNode()->GetValue() : currentParent_;
11998bf80f4bSopenharmony_ci            }
12008bf80f4bSopenharmony_ci        }
12018bf80f4bSopenharmony_ci
12028bf80f4bSopenharmony_ci        nodeInterface->Initialize(ecsScene, ecsObject, parent, nodePath, nodeName, sceneHolder_, entity);
12038bf80f4bSopenharmony_ci    }
12048bf80f4bSopenharmony_ci
12058bf80f4bSopenharmony_ci    void UpdateCachedNodePath(const SCENE_NS::INode::Ptr& node) override
12068bf80f4bSopenharmony_ci    {
12078bf80f4bSopenharmony_ci        if (node) {
12088bf80f4bSopenharmony_ci            if (interface_cast<META_NS::IObject>(node)->GetClassId() != SCENE_NS::ClassId::Node) {
12098bf80f4bSopenharmony_ci                UpdateCachedReference(node);
12108bf80f4bSopenharmony_ci                return;
12118bf80f4bSopenharmony_ci            }
12128bf80f4bSopenharmony_ci
12138bf80f4bSopenharmony_ci            bool found = false;
12148bf80f4bSopenharmony_ci
12158bf80f4bSopenharmony_ci            for (auto&& ite : nodes_) {
12168bf80f4bSopenharmony_ci                if (ite.second == node) {
12178bf80f4bSopenharmony_ci                    nodes_.erase(ite.first);
12188bf80f4bSopenharmony_ci                    break;
12198bf80f4bSopenharmony_ci                }
12208bf80f4bSopenharmony_ci            }
12218bf80f4bSopenharmony_ci
12228bf80f4bSopenharmony_ci            BASE_NS::string pathString = node->Path()->GetValue();
12238bf80f4bSopenharmony_ci            pathString.append(node->Name()->GetValue());
12248bf80f4bSopenharmony_ci            nodes_[pathString] = node;
12258bf80f4bSopenharmony_ci        }
12268bf80f4bSopenharmony_ci    }
12278bf80f4bSopenharmony_ci
12288bf80f4bSopenharmony_ci    void SetCacheEnabled(const SCENE_NS::INode::Ptr& node, bool enabled) override
12298bf80f4bSopenharmony_ci    {
12308bf80f4bSopenharmony_ci        if (!node) {
12318bf80f4bSopenharmony_ci            return;
12328bf80f4bSopenharmony_ci        }
12338bf80f4bSopenharmony_ci
12348bf80f4bSopenharmony_ci        if (enabled) {
12358bf80f4bSopenharmony_ci            BASE_NS::string pathString = node->Path()->GetValue();
12368bf80f4bSopenharmony_ci            pathString.append(node->Name()->GetValue());
12378bf80f4bSopenharmony_ci            nodes_[pathString] = node;
12388bf80f4bSopenharmony_ci        } else {
12398bf80f4bSopenharmony_ci            for (auto&& ite : nodes_) {
12408bf80f4bSopenharmony_ci                if (ite.second == node) {
12418bf80f4bSopenharmony_ci                    nodes_.erase(ite.first);
12428bf80f4bSopenharmony_ci                    break;
12438bf80f4bSopenharmony_ci                }
12448bf80f4bSopenharmony_ci            }
12458bf80f4bSopenharmony_ci        }
12468bf80f4bSopenharmony_ci    }
12478bf80f4bSopenharmony_ci
12488bf80f4bSopenharmony_ci    typedef BASE_NS::shared_ptr<CORE_NS::IInterface> (*fun)(SceneImpl* me, const BASE_NS::string_view);
12498bf80f4bSopenharmony_ci
12508bf80f4bSopenharmony_ci    static BASE_NS::shared_ptr<CORE_NS::IInterface> relmat(SceneImpl* me, const BASE_NS::string_view p)
12518bf80f4bSopenharmony_ci    {
12528bf80f4bSopenharmony_ci        return me->ReleaseMaterial(p);
12538bf80f4bSopenharmony_ci    }
12548bf80f4bSopenharmony_ci    static BASE_NS::shared_ptr<CORE_NS::IInterface> relmesh(SceneImpl* me, const BASE_NS::string_view p)
12558bf80f4bSopenharmony_ci    {
12568bf80f4bSopenharmony_ci        return me->ReleaseMesh(p);
12578bf80f4bSopenharmony_ci    }
12588bf80f4bSopenharmony_ci    static BASE_NS::shared_ptr<CORE_NS::IInterface> relanim(SceneImpl* me, const BASE_NS::string_view p)
12598bf80f4bSopenharmony_ci    {
12608bf80f4bSopenharmony_ci        return me->ReleaseAnimation(p);
12618bf80f4bSopenharmony_ci    }
12628bf80f4bSopenharmony_ci    static BASE_NS::shared_ptr<CORE_NS::IInterface> relnode(SceneImpl* me, const BASE_NS::string_view p)
12638bf80f4bSopenharmony_ci    {
12648bf80f4bSopenharmony_ci        return me->ReleaseNode(p);
12658bf80f4bSopenharmony_ci    }
12668bf80f4bSopenharmony_ci    template<typename Type>
12678bf80f4bSopenharmony_ci    bool CacheNode(const char* const tname, const SCENE_NS::INode::Ptr& node,
12688bf80f4bSopenharmony_ci        BASE_NS::unordered_map<BASE_NS::string, typename Type::WeakPtr>& cache, fun func)
12698bf80f4bSopenharmony_ci    {
12708bf80f4bSopenharmony_ci        if (auto typed = interface_pointer_cast<Type>(node)) {
12718bf80f4bSopenharmony_ci            BASE_NS::string uri;
12728bf80f4bSopenharmony_ci            bool found = false;
12738bf80f4bSopenharmony_ci
12748bf80f4bSopenharmony_ci            if (node->GetInterface(SCENE_NS::ICamera::UID)) {
12758bf80f4bSopenharmony_ci                // handle camera naming..
12768bf80f4bSopenharmony_ci                uri = node->Path()->GetValue();
12778bf80f4bSopenharmony_ci                uri.append(node->Name()->GetValue());
12788bf80f4bSopenharmony_ci
12798bf80f4bSopenharmony_ci            } else {
12808bf80f4bSopenharmony_ci                auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(typed);
12818bf80f4bSopenharmony_ci                uri = sceneHolder_->GetResourceId(ecsObject->GetEntity());
12828bf80f4bSopenharmony_ci                if (uri.empty()) {
12838bf80f4bSopenharmony_ci                    uri = node->Name()->GetValue() + ":" + BASE_NS::to_hex(ecsObject->GetEntity().id);
12848bf80f4bSopenharmony_ci                }
12858bf80f4bSopenharmony_ci            }
12868bf80f4bSopenharmony_ci
12878bf80f4bSopenharmony_ci            for (auto&& ite : cache) {
12888bf80f4bSopenharmony_ci                if (ite.second.lock() == typed) {
12898bf80f4bSopenharmony_ci                    if (uri != ite.first) {
12908bf80f4bSopenharmony_ci                        cache[uri] = interface_pointer_cast<Type>(func(this, ite.first));
12918bf80f4bSopenharmony_ci                        SCENE_PLUGIN_VERBOSE_LOG("Updating cached reference of %s: %s", tname, uri.c_str());
12928bf80f4bSopenharmony_ci                    }
12938bf80f4bSopenharmony_ci                    found = true;
12948bf80f4bSopenharmony_ci                    break; // reference is valid so return true regardless if the node was moved
12958bf80f4bSopenharmony_ci                }
12968bf80f4bSopenharmony_ci            }
12978bf80f4bSopenharmony_ci
12988bf80f4bSopenharmony_ci            if (!found) {
12998bf80f4bSopenharmony_ci                SCENE_PLUGIN_VERBOSE_LOG("Caching reference of %s: %s", tname, uri.c_str());
13008bf80f4bSopenharmony_ci                cache[uri] = typed;
13018bf80f4bSopenharmony_ci            }
13028bf80f4bSopenharmony_ci            return true;
13038bf80f4bSopenharmony_ci        }
13048bf80f4bSopenharmony_ci        return false;
13058bf80f4bSopenharmony_ci    }
13068bf80f4bSopenharmony_ci    void UpdateCachedReference(const SCENE_NS::INode::Ptr& node) override
13078bf80f4bSopenharmony_ci    {
13088bf80f4bSopenharmony_ci        if (node) {
13098bf80f4bSopenharmony_ci            if (CacheNode<SCENE_NS::IMaterial>("material", node, materials_, relmat) ||
13108bf80f4bSopenharmony_ci                CacheNode<SCENE_NS::IMesh>("mesh", node, meshes_, relmesh) ||
13118bf80f4bSopenharmony_ci                CacheNode<META_NS::IAnimation>("animation", node, animations_, relanim)) {
13128bf80f4bSopenharmony_ci                // completed. (meshes, materials and animations are not added to node list)
13138bf80f4bSopenharmony_ci                return;
13148bf80f4bSopenharmony_ci            }
13158bf80f4bSopenharmony_ci            if (CacheNode<SCENE_NS::ICamera>("camera", node, cameras_, relnode)) {
13168bf80f4bSopenharmony_ci                // camera cache updated.
13178bf80f4bSopenharmony_ci                auto* obj = interface_cast<SCENE_NS::IEcsObject>(node);
13188bf80f4bSopenharmony_ci                if (obj) {
13198bf80f4bSopenharmony_ci                    auto entity = obj->GetEntity();
13208bf80f4bSopenharmony_ci                    if (CORE_NS::EntityUtil::IsValid(entity)) {
13218bf80f4bSopenharmony_ci                        sceneHolder_->AddCamera(entity);
13228bf80f4bSopenharmony_ci                    } else {
13238bf80f4bSopenharmony_ci                        CORE_LOG_V("camera has no entity id yet");
13248bf80f4bSopenharmony_ci                    }
13258bf80f4bSopenharmony_ci                } else {
13268bf80f4bSopenharmony_ci                    CORE_LOG_V("camera has no IEcsObject");
13278bf80f4bSopenharmony_ci                }
13288bf80f4bSopenharmony_ci            }
13298bf80f4bSopenharmony_ci            // update node cache.
13308bf80f4bSopenharmony_ci
13318bf80f4bSopenharmony_ci            bool found = false;
13328bf80f4bSopenharmony_ci            BASE_NS::string uri = node->Path()->GetValue();
13338bf80f4bSopenharmony_ci            uri.append(node->Name()->GetValue());
13348bf80f4bSopenharmony_ci
13358bf80f4bSopenharmony_ci            for (auto&& ite : nodes_) {
13368bf80f4bSopenharmony_ci                if (ite.second == node) {
13378bf80f4bSopenharmony_ci                    if (uri != ite.first) {
13388bf80f4bSopenharmony_ci                        nodes_[uri] = ReleaseNode(ite.first);
13398bf80f4bSopenharmony_ci                    }
13408bf80f4bSopenharmony_ci                    found = true;
13418bf80f4bSopenharmony_ci                    break; // reference is valid so return true regardless if the node was moved
13428bf80f4bSopenharmony_ci                }
13438bf80f4bSopenharmony_ci            }
13448bf80f4bSopenharmony_ci
13458bf80f4bSopenharmony_ci            if (!found) {
13468bf80f4bSopenharmony_ci                nodes_[uri] = node;
13478bf80f4bSopenharmony_ci            }
13488bf80f4bSopenharmony_ci        }
13498bf80f4bSopenharmony_ci    }
13508bf80f4bSopenharmony_ci
13518bf80f4bSopenharmony_ci    // Release Node Reference from cache
13528bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr ReleaseNode(const BASE_NS::string_view name) override
13538bf80f4bSopenharmony_ci    {
13548bf80f4bSopenharmony_ci        SCENE_NS::INode::Ptr ret {};
13558bf80f4bSopenharmony_ci        if (auto ite = nodes_.extract(BASE_NS::string(name)); !ite.empty()) {
13568bf80f4bSopenharmony_ci            ret = BASE_NS::move(ite.mapped());
13578bf80f4bSopenharmony_ci            if (auto privateIntf = interface_cast<INodeEcsInterfacePrivate>(ret)) {
13588bf80f4bSopenharmony_ci                privateIntf->ClaimOwnershipOfEntity(true);
13598bf80f4bSopenharmony_ci            }
13608bf80f4bSopenharmony_ci            RemoveNodeFromCurrentContainer(ret);
13618bf80f4bSopenharmony_ci            if (auto cam = interface_pointer_cast<SCENE_NS::ICamera>(ret)) {
13628bf80f4bSopenharmony_ci                ReleaseCamera(name);
13638bf80f4bSopenharmony_ci            }
13648bf80f4bSopenharmony_ci        }
13658bf80f4bSopenharmony_ci        return ret;
13668bf80f4bSopenharmony_ci    }
13678bf80f4bSopenharmony_ci    void ReleaseNode(const SCENE_NS::INode::Ptr& node) override
13688bf80f4bSopenharmony_ci    {
13698bf80f4bSopenharmony_ci        if (node) {
13708bf80f4bSopenharmony_ci            ReleaseNode(node->Path()->GetValue() + node->Name()->GetValue());
13718bf80f4bSopenharmony_ci        }
13728bf80f4bSopenharmony_ci    }
13738bf80f4bSopenharmony_ci
13748bf80f4bSopenharmony_ci    SCENE_NS::IMaterial::Ptr ReleaseMaterial(const BASE_NS::string_view name) override
13758bf80f4bSopenharmony_ci    {
13768bf80f4bSopenharmony_ci        SCENE_NS::IMaterial::Ptr ret {};
13778bf80f4bSopenharmony_ci        if (auto ite = materials_.find(name); ite != materials_.end()) {
13788bf80f4bSopenharmony_ci            ret = ite->second.lock();
13798bf80f4bSopenharmony_ci            materials_.erase(ite);
13808bf80f4bSopenharmony_ci        }
13818bf80f4bSopenharmony_ci        return ret;
13828bf80f4bSopenharmony_ci    }
13838bf80f4bSopenharmony_ci
13848bf80f4bSopenharmony_ci    SCENE_NS::IMesh::Ptr ReleaseMesh(const BASE_NS::string_view name) override
13858bf80f4bSopenharmony_ci    {
13868bf80f4bSopenharmony_ci        SCENE_NS::IMesh::Ptr ret {};
13878bf80f4bSopenharmony_ci        if (auto ite = meshes_.find(name); ite != meshes_.end()) {
13888bf80f4bSopenharmony_ci            ret = ite->second.lock();
13898bf80f4bSopenharmony_ci            meshes_.erase(ite);
13908bf80f4bSopenharmony_ci        }
13918bf80f4bSopenharmony_ci
13928bf80f4bSopenharmony_ci        return ret;
13938bf80f4bSopenharmony_ci    }
13948bf80f4bSopenharmony_ci
13958bf80f4bSopenharmony_ci    META_NS::IAnimation::Ptr ReleaseAnimation(const BASE_NS::string_view name) override
13968bf80f4bSopenharmony_ci    {
13978bf80f4bSopenharmony_ci        META_NS::IAnimation::Ptr ret {};
13988bf80f4bSopenharmony_ci        if (auto ite = animations_.find(name); ite != animations_.end()) {
13998bf80f4bSopenharmony_ci            ret = ite->second.lock();
14008bf80f4bSopenharmony_ci            animations_.erase(ite);
14018bf80f4bSopenharmony_ci        }
14028bf80f4bSopenharmony_ci        return ret;
14038bf80f4bSopenharmony_ci    }
14048bf80f4bSopenharmony_ci
14058bf80f4bSopenharmony_ci    SCENE_NS::ICamera::Ptr ReleaseCamera(const BASE_NS::string_view name)
14068bf80f4bSopenharmony_ci    {
14078bf80f4bSopenharmony_ci        SCENE_NS::ICamera::Ptr ret {};
14088bf80f4bSopenharmony_ci        if (auto ite = cameras_.find(name); ite != cameras_.end()) {
14098bf80f4bSopenharmony_ci            ret = ite->second.lock();
14108bf80f4bSopenharmony_ci            // make sure the rendertarget/bitmap is also released.
14118bf80f4bSopenharmony_ci            SetBitmap(nullptr, ret);
14128bf80f4bSopenharmony_ci            cameras_.erase(ite);
14138bf80f4bSopenharmony_ci        }
14148bf80f4bSopenharmony_ci        return ret;
14158bf80f4bSopenharmony_ci    }
14168bf80f4bSopenharmony_ci
14178bf80f4bSopenharmony_ci    SCENE_NS::IMesh::Ptr CreateMeshFromArraysI16(
14188bf80f4bSopenharmony_ci        const BASE_NS::string_view name, SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays) override
14198bf80f4bSopenharmony_ci    {
14208bf80f4bSopenharmony_ci        return CreateMeshFromArrays<uint16_t>(name, arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT16);
14218bf80f4bSopenharmony_ci    }
14228bf80f4bSopenharmony_ci
14238bf80f4bSopenharmony_ci    SCENE_NS::IMesh::Ptr CreateMeshFromArraysI32(
14248bf80f4bSopenharmony_ci        const BASE_NS::string_view name, SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays) override
14258bf80f4bSopenharmony_ci    {
14268bf80f4bSopenharmony_ci        return CreateMeshFromArrays<uint32_t>(name, arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT32);
14278bf80f4bSopenharmony_ci    }
14288bf80f4bSopenharmony_ci
14298bf80f4bSopenharmony_ci    template<typename IndicesType>
14308bf80f4bSopenharmony_ci    SCENE_NS::IMesh::Ptr CreateMeshFromArrays(const BASE_NS::string_view name,
14318bf80f4bSopenharmony_ci        SCENE_NS::MeshGeometryArrayPtr<IndicesType> arrays, RENDER_NS::IndexType indexType)
14328bf80f4bSopenharmony_ci    {
14338bf80f4bSopenharmony_ci        auto nameString = BASE_NS::shared_ptr(new BASE_NS::string(name.data(), name.size()));
14348bf80f4bSopenharmony_ci
14358bf80f4bSopenharmony_ci        AddEngineTask(MakeTask(
14368bf80f4bSopenharmony_ci                          [arrays, nameString, indexType](auto sceneHolder) {
14378bf80f4bSopenharmony_ci                              auto meshEntity = sceneHolder->template CreateMeshFromArrays<IndicesType>(
14388bf80f4bSopenharmony_ci                                  *nameString, arrays, indexType);
14398bf80f4bSopenharmony_ci                              if (!sceneHolder->IsAsync()) {
14408bf80f4bSopenharmony_ci                                  *nameString = sceneHolder->GetResourceId(meshEntity);
14418bf80f4bSopenharmony_ci                              }
14428bf80f4bSopenharmony_ci
14438bf80f4bSopenharmony_ci                              return false;
14448bf80f4bSopenharmony_ci                          },
14458bf80f4bSopenharmony_ci                          sceneHolder_),
14468bf80f4bSopenharmony_ci            false);
14478bf80f4bSopenharmony_ci        return GetMesh(*nameString);
14488bf80f4bSopenharmony_ci    }
14498bf80f4bSopenharmony_ci
14508bf80f4bSopenharmony_ci    void InstantiateMaterialProxies() override
14518bf80f4bSopenharmony_ci    {
14528bf80f4bSopenharmony_ci        AddEngineTask(MakeTask(
14538bf80f4bSopenharmony_ci                          [me = BASE_NS::weak_ptr(GetSelf<SCENE_NS::IScene>())](auto sceneHolder) {
14548bf80f4bSopenharmony_ci                              auto ids = sceneHolder->ListMaterialNames();
14558bf80f4bSopenharmony_ci                              sceneHolder->QueueApplicationTask(MakeTask(
14568bf80f4bSopenharmony_ci                                                                    [ids](auto self) {
14578bf80f4bSopenharmony_ci                                                                        for (auto& id : *ids) {
14588bf80f4bSopenharmony_ci                                                                            self->GetMaterial(id);
14598bf80f4bSopenharmony_ci                                                                        }
14608bf80f4bSopenharmony_ci                                                                        return false;
14618bf80f4bSopenharmony_ci                                                                    },
14628bf80f4bSopenharmony_ci                                                                    me),
14638bf80f4bSopenharmony_ci                                  false);
14648bf80f4bSopenharmony_ci                              return false;
14658bf80f4bSopenharmony_ci                          },
14668bf80f4bSopenharmony_ci                          sceneHolder_),
14678bf80f4bSopenharmony_ci            false);
14688bf80f4bSopenharmony_ci    }
14698bf80f4bSopenharmony_ci
14708bf80f4bSopenharmony_ci    void InstantiateMeshProxies() override
14718bf80f4bSopenharmony_ci    {
14728bf80f4bSopenharmony_ci        AddEngineTask(MakeTask(
14738bf80f4bSopenharmony_ci                          [me = BASE_NS::weak_ptr(GetSelf<SCENE_NS::IScene>())](auto sceneHolder) {
14748bf80f4bSopenharmony_ci                              auto ids = sceneHolder->ListMeshNames();
14758bf80f4bSopenharmony_ci                              sceneHolder->QueueApplicationTask(MakeTask(
14768bf80f4bSopenharmony_ci                                                                    [ids](auto self) {
14778bf80f4bSopenharmony_ci                                                                        for (auto& id : *ids) {
14788bf80f4bSopenharmony_ci                                                                            self->GetMesh(id);
14798bf80f4bSopenharmony_ci                                                                        }
14808bf80f4bSopenharmony_ci
14818bf80f4bSopenharmony_ci                                                                        return false;
14828bf80f4bSopenharmony_ci                                                                    },
14838bf80f4bSopenharmony_ci                                                                    me),
14848bf80f4bSopenharmony_ci                                  false);
14858bf80f4bSopenharmony_ci
14868bf80f4bSopenharmony_ci                              return false;
14878bf80f4bSopenharmony_ci                          },
14888bf80f4bSopenharmony_ci                          sceneHolder_),
14898bf80f4bSopenharmony_ci            false);
14908bf80f4bSopenharmony_ci    }
14918bf80f4bSopenharmony_ci
14928bf80f4bSopenharmony_cipublic:
14938bf80f4bSopenharmony_ci    ~SceneImpl() override
14948bf80f4bSopenharmony_ci    {
14958bf80f4bSopenharmony_ci        allAnims_.clear();
14968bf80f4bSopenharmony_ci        if (sceneHolder_) {
14978bf80f4bSopenharmony_ci            DetachScene();
14988bf80f4bSopenharmony_ci        }
14998bf80f4bSopenharmony_ci
15008bf80f4bSopenharmony_ci        cameraNodePtr_.reset();
15018bf80f4bSopenharmony_ci        rootNodePtr_.reset();
15028bf80f4bSopenharmony_ci
15038bf80f4bSopenharmony_ci        UnsubscribeFromPropertyChanges();
15048bf80f4bSopenharmony_ci
15058bf80f4bSopenharmony_ci        if (sceneHolder_) {
15068bf80f4bSopenharmony_ci            sceneHolder_->Uninitialize();
15078bf80f4bSopenharmony_ci            sceneHolder_.reset();
15088bf80f4bSopenharmony_ci        }
15098bf80f4bSopenharmony_ci    }
15108bf80f4bSopenharmony_ci
15118bf80f4bSopenharmony_ci    void SetEcsInitializationCallback(IPrepareSceneForInitialization::WeakPtr callback) override
15128bf80f4bSopenharmony_ci    {
15138bf80f4bSopenharmony_ci        if (sceneHolder_) {
15148bf80f4bSopenharmony_ci            sceneHolder_->SetEcsInitializationCallback(callback);
15158bf80f4bSopenharmony_ci        } else {
15168bf80f4bSopenharmony_ci            CORE_LOG_W("%s: sceneholder does not exist, cannot set callback", __func__);
15178bf80f4bSopenharmony_ci        }
15188bf80f4bSopenharmony_ci    }
15198bf80f4bSopenharmony_ci
15208bf80f4bSopenharmony_ci    SCENE_NS::IPickingResult::Ptr GetWorldAABB(const BASE_NS::Math::Mat4X4& world, const BASE_NS::Math::Vec3& aabbMin,
15218bf80f4bSopenharmony_ci        const BASE_NS::Math::Vec3& aabbMax) override
15228bf80f4bSopenharmony_ci    {
15238bf80f4bSopenharmony_ci        auto ret = META_NS::GetObjectRegistry().Create<SCENE_NS::IPickingResult>(SCENE_NS::ClassId::PendingVec3Request);
15248bf80f4bSopenharmony_ci        if (ret) {
15258bf80f4bSopenharmony_ci            AddEngineTask(
15268bf80f4bSopenharmony_ci                META_NS::MakeCallback<META_NS::ITaskQueueTask>([w = world, min = aabbMin, max = aabbMax,
15278bf80f4bSopenharmony_ci                                                                   weakRet = BASE_NS::weak_ptr(ret),
15288bf80f4bSopenharmony_ci                                                                   weakSh = BASE_NS::weak_ptr(sceneHolder_)]() {
15298bf80f4bSopenharmony_ci                    if (auto sh = weakSh.lock()) {
15308bf80f4bSopenharmony_ci                        if (auto ret = weakRet.lock()) {
15318bf80f4bSopenharmony_ci                            if (sh->GetWorldAABB(ret, w, min, max)) {
15328bf80f4bSopenharmony_ci                                sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet]() {
15338bf80f4bSopenharmony_ci                                    if (auto writable =
15348bf80f4bSopenharmony_ci                                            interface_pointer_cast<SCENE_NS::IPendingRequestData<BASE_NS::Math::Vec3>>(
15358bf80f4bSopenharmony_ci                                                weakRet)) {
15368bf80f4bSopenharmony_ci                                        writable->MarkReady();
15378bf80f4bSopenharmony_ci                                    }
15388bf80f4bSopenharmony_ci                                    return false;
15398bf80f4bSopenharmony_ci                                }),
15408bf80f4bSopenharmony_ci                                    false);
15418bf80f4bSopenharmony_ci                            }
15428bf80f4bSopenharmony_ci                        }
15438bf80f4bSopenharmony_ci                    }
15448bf80f4bSopenharmony_ci                    return false;
15458bf80f4bSopenharmony_ci                }),
15468bf80f4bSopenharmony_ci                false);
15478bf80f4bSopenharmony_ci            return ret;
15488bf80f4bSopenharmony_ci        }
15498bf80f4bSopenharmony_ci        return SCENE_NS::IPickingResult::Ptr();
15508bf80f4bSopenharmony_ci    }
15518bf80f4bSopenharmony_ci
15528bf80f4bSopenharmony_ci    SCENE_NS::IRayCastResult::Ptr RayCast(
15538bf80f4bSopenharmony_ci        const BASE_NS::Math::Vec3& start, const BASE_NS::Math::Vec3& direction) override
15548bf80f4bSopenharmony_ci    {
15558bf80f4bSopenharmony_ci        auto ret =
15568bf80f4bSopenharmony_ci            META_NS::GetObjectRegistry().Create<SCENE_NS::IRayCastResult>(SCENE_NS::ClassId::PendingDistanceRequest);
15578bf80f4bSopenharmony_ci        if (ret) {
15588bf80f4bSopenharmony_ci            AddEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([origin = start, dir = direction,
15598bf80f4bSopenharmony_ci                                                                             weakRet = BASE_NS::weak_ptr(ret),
15608bf80f4bSopenharmony_ci                                                                             weakSh = BASE_NS::weak_ptr(sceneHolder_),
15618bf80f4bSopenharmony_ci                                                                             weakSelf = BASE_NS::weak_ptr(
15628bf80f4bSopenharmony_ci                                                                                 GetSelf<SCENE_NS::IScene>())]() {
15638bf80f4bSopenharmony_ci                if (auto sh = weakSh.lock()) {
15648bf80f4bSopenharmony_ci                    if (auto ret = weakRet.lock()) {
15658bf80f4bSopenharmony_ci                        if (sh->RayCast(ret, origin, dir)) {
15668bf80f4bSopenharmony_ci                            sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet,
15678bf80f4bSopenharmony_ci                                                                                                        weakSelf]() {
15688bf80f4bSopenharmony_ci                                if (auto writable =
15698bf80f4bSopenharmony_ci                                        interface_pointer_cast<SCENE_NS::IPendingRequestData<SCENE_NS::NodeDistance>>(
15708bf80f4bSopenharmony_ci                                            weakRet)) {
15718bf80f4bSopenharmony_ci                                    if (auto self = weakSelf.lock()) {
15728bf80f4bSopenharmony_ci                                        // resolve proxy nodes
15738bf80f4bSopenharmony_ci                                        for (size_t ii = writable->MetaData().size(); ii > 0;) {
15748bf80f4bSopenharmony_ci                                            --ii;
15758bf80f4bSopenharmony_ci                                            writable->MutableData().at(ii).node =
15768bf80f4bSopenharmony_ci                                                self->GetNode(writable->MetaData().at(ii));
15778bf80f4bSopenharmony_ci                                        }
15788bf80f4bSopenharmony_ci                                        writable->MarkReady();
15798bf80f4bSopenharmony_ci                                    }
15808bf80f4bSopenharmony_ci                                }
15818bf80f4bSopenharmony_ci                                return false;
15828bf80f4bSopenharmony_ci                            }),
15838bf80f4bSopenharmony_ci                                false);
15848bf80f4bSopenharmony_ci                        }
15858bf80f4bSopenharmony_ci                    }
15868bf80f4bSopenharmony_ci                }
15878bf80f4bSopenharmony_ci                return false;
15888bf80f4bSopenharmony_ci            }),
15898bf80f4bSopenharmony_ci                false);
15908bf80f4bSopenharmony_ci            return ret;
15918bf80f4bSopenharmony_ci        }
15928bf80f4bSopenharmony_ci        return SCENE_NS::IRayCastResult::Ptr();
15938bf80f4bSopenharmony_ci    }
15948bf80f4bSopenharmony_ci
15958bf80f4bSopenharmony_ci    SCENE_NS::IRayCastResult::Ptr RayCast(
15968bf80f4bSopenharmony_ci        const BASE_NS::Math::Vec3& start, const BASE_NS::Math::Vec3& direction, uint64_t layerMask) override
15978bf80f4bSopenharmony_ci    {
15988bf80f4bSopenharmony_ci        auto ret =
15998bf80f4bSopenharmony_ci            META_NS::GetObjectRegistry().Create<SCENE_NS::IRayCastResult>(SCENE_NS::ClassId::PendingDistanceRequest);
16008bf80f4bSopenharmony_ci        if (ret) {
16018bf80f4bSopenharmony_ci            AddEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([origin = start, dir = direction, layerMask,
16028bf80f4bSopenharmony_ci                                                                             weakRet = BASE_NS::weak_ptr(ret),
16038bf80f4bSopenharmony_ci                                                                             weakSh = BASE_NS::weak_ptr(sceneHolder_),
16048bf80f4bSopenharmony_ci                                                                             weakSelf = BASE_NS::weak_ptr(
16058bf80f4bSopenharmony_ci                                                                                 GetSelf<SCENE_NS::IScene>())]() {
16068bf80f4bSopenharmony_ci                if (auto sh = weakSh.lock()) {
16078bf80f4bSopenharmony_ci                    if (auto ret = weakRet.lock()) {
16088bf80f4bSopenharmony_ci                        if (sh->RayCast(ret, origin, dir, layerMask)) {
16098bf80f4bSopenharmony_ci                            sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet,
16108bf80f4bSopenharmony_ci                                                                                                        weakSelf]() {
16118bf80f4bSopenharmony_ci                                if (auto writable =
16128bf80f4bSopenharmony_ci                                        interface_pointer_cast<SCENE_NS::IPendingRequestData<SCENE_NS::NodeDistance>>(
16138bf80f4bSopenharmony_ci                                            weakRet)) {
16148bf80f4bSopenharmony_ci                                    if (auto self = weakSelf.lock()) {
16158bf80f4bSopenharmony_ci                                        // resolve proxy nodes
16168bf80f4bSopenharmony_ci                                        for (size_t ii = writable->MetaData().size(); ii > 0;) {
16178bf80f4bSopenharmony_ci                                            --ii;
16188bf80f4bSopenharmony_ci                                            writable->MutableData().at(ii).node =
16198bf80f4bSopenharmony_ci                                                self->GetNode(writable->MetaData().at(ii));
16208bf80f4bSopenharmony_ci                                        }
16218bf80f4bSopenharmony_ci                                        writable->MarkReady();
16228bf80f4bSopenharmony_ci                                    }
16238bf80f4bSopenharmony_ci                                }
16248bf80f4bSopenharmony_ci                                return false;
16258bf80f4bSopenharmony_ci                            }),
16268bf80f4bSopenharmony_ci                                false);
16278bf80f4bSopenharmony_ci                        }
16288bf80f4bSopenharmony_ci                    }
16298bf80f4bSopenharmony_ci                }
16308bf80f4bSopenharmony_ci                return false;
16318bf80f4bSopenharmony_ci            }),
16328bf80f4bSopenharmony_ci                false);
16338bf80f4bSopenharmony_ci            return ret;
16348bf80f4bSopenharmony_ci        }
16358bf80f4bSopenharmony_ci        return SCENE_NS::IRayCastResult::Ptr();
16368bf80f4bSopenharmony_ci    }
16378bf80f4bSopenharmony_ci    BASE_NS::vector<CORE_NS::Entity> RenderCameras() override
16388bf80f4bSopenharmony_ci    {
16398bf80f4bSopenharmony_ci        if (sceneHolder_) {
16408bf80f4bSopenharmony_ci            return sceneHolder_->RenderCameras();
16418bf80f4bSopenharmony_ci        }
16428bf80f4bSopenharmony_ci        return {};
16438bf80f4bSopenharmony_ci    }
16448bf80f4bSopenharmony_ci    void ActivateCamera(const SCENE_NS::ICamera::Ptr& camera) override
16458bf80f4bSopenharmony_ci    {
16468bf80f4bSopenharmony_ci        if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
16478bf80f4bSopenharmony_ci            auto ent = e->GetEntity();
16488bf80f4bSopenharmony_ci            sceneHolder_->ActivateCamera(ent);
16498bf80f4bSopenharmony_ci        }
16508bf80f4bSopenharmony_ci    }
16518bf80f4bSopenharmony_ci
16528bf80f4bSopenharmony_ci    void DeactivateCamera(const SCENE_NS::ICamera::Ptr& camera) override
16538bf80f4bSopenharmony_ci    {
16548bf80f4bSopenharmony_ci        if (!camera) {
16558bf80f4bSopenharmony_ci            return;
16568bf80f4bSopenharmony_ci        }
16578bf80f4bSopenharmony_ci
16588bf80f4bSopenharmony_ci        if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
16598bf80f4bSopenharmony_ci            sceneHolder_->DeactivateCamera(e->GetEntity());
16608bf80f4bSopenharmony_ci        }
16618bf80f4bSopenharmony_ci    }
16628bf80f4bSopenharmony_ci    bool IsCameraActive(const SCENE_NS::ICamera::Ptr& camera) override
16638bf80f4bSopenharmony_ci    {
16648bf80f4bSopenharmony_ci        if (!camera) {
16658bf80f4bSopenharmony_ci            return false;
16668bf80f4bSopenharmony_ci        }
16678bf80f4bSopenharmony_ci
16688bf80f4bSopenharmony_ci        if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
16698bf80f4bSopenharmony_ci            return sceneHolder_->IsCameraActive(e->GetEntity());
16708bf80f4bSopenharmony_ci        }
16718bf80f4bSopenharmony_ci        return false;
16728bf80f4bSopenharmony_ci    }
16738bf80f4bSopenharmony_ci
16748bf80f4bSopenharmony_ciprivate:
16758bf80f4bSopenharmony_ci    void SubscribeToPropertyChanges()
16768bf80f4bSopenharmony_ci    {
16778bf80f4bSopenharmony_ci        if (RootNode()) {
16788bf80f4bSopenharmony_ci            rootNodeChangedToken_ = RootNode()->OnChanged()->AddHandler(
16798bf80f4bSopenharmony_ci                META_NS::MakeCallback<META_NS::IOnChanged>(this, &SceneImpl::OnRootNodeChanged));
16808bf80f4bSopenharmony_ci        }
16818bf80f4bSopenharmony_ci
16828bf80f4bSopenharmony_ci        if (Uri()) {
16838bf80f4bSopenharmony_ci            uriHandlerToken_ = Uri()->OnChanged()->AddHandler(
16848bf80f4bSopenharmony_ci                META_NS::MakeCallback<META_NS::IOnChanged>([this]() { this->Load(Uri()->GetValue()); }));
16858bf80f4bSopenharmony_ci        }
16868bf80f4bSopenharmony_ci
16878bf80f4bSopenharmony_ci        // Start listening changes of the scene controller properties. These may go to different place some day
16888bf80f4bSopenharmony_ci        if (SystemGraphUri()) {
16898bf80f4bSopenharmony_ci            systemGraphUriHandlerToken_ = SystemGraphUri()->OnChanged()->AddHandler(
16908bf80f4bSopenharmony_ci                META_NS::MakeCallback<META_NS::IOnChanged>(this, &SceneImpl::onSystemGraphUriChanged));
16918bf80f4bSopenharmony_ci        }
16928bf80f4bSopenharmony_ci    }
16938bf80f4bSopenharmony_ci
16948bf80f4bSopenharmony_ci    void UnsubscribeFromPropertyChanges()
16958bf80f4bSopenharmony_ci    {
16968bf80f4bSopenharmony_ci        if (Uri()) {
16978bf80f4bSopenharmony_ci            Uri()->OnChanged()->RemoveHandler(uriHandlerToken_);
16988bf80f4bSopenharmony_ci            uriHandlerToken_ = {};
16998bf80f4bSopenharmony_ci        }
17008bf80f4bSopenharmony_ci
17018bf80f4bSopenharmony_ci        if (RootNode()) {
17028bf80f4bSopenharmony_ci            RootNode()->OnChanged()->RemoveHandler(rootNodeChangedToken_);
17038bf80f4bSopenharmony_ci            rootNodeChangedToken_ = {};
17048bf80f4bSopenharmony_ci        }
17058bf80f4bSopenharmony_ci
17068bf80f4bSopenharmony_ci        if (SystemGraphUri()) {
17078bf80f4bSopenharmony_ci            SystemGraphUri()->OnChanged()->RemoveHandler(systemGraphUriHandlerToken_);
17088bf80f4bSopenharmony_ci            systemGraphUriHandlerToken_ = {};
17098bf80f4bSopenharmony_ci        }
17108bf80f4bSopenharmony_ci
17118bf80f4bSopenharmony_ci        if (Asynchronous()) {
17128bf80f4bSopenharmony_ci            Asynchronous()->OnChanged()->RemoveHandler(asyncChangedToken_);
17138bf80f4bSopenharmony_ci            asyncChangedToken_ = {};
17148bf80f4bSopenharmony_ci        }
17158bf80f4bSopenharmony_ci    }
17168bf80f4bSopenharmony_ci
17178bf80f4bSopenharmony_ci    void OnRootNodeChanged()
17188bf80f4bSopenharmony_ci    {
17198bf80f4bSopenharmony_ci        auto contentObject = interface_pointer_cast<META_NS::IObject>(RootNode()->GetValue());
17208bf80f4bSopenharmony_ci        contentImpl_->SetContent(contentObject);
17218bf80f4bSopenharmony_ci
17228bf80f4bSopenharmony_ci        META_NS::HierarchyChangeModeValue changeMode;
17238bf80f4bSopenharmony_ci        changeMode.Set(META_NS::HierarchyChangeMode::NOTIFY_CONTAINER);
17248bf80f4bSopenharmony_ci        hierarchyController_->SetTarget(contentObject, changeMode);
17258bf80f4bSopenharmony_ci    }
17268bf80f4bSopenharmony_ci
17278bf80f4bSopenharmony_ci    META_NS::IEvent::Token systemGraphUriHandlerToken_ {};
17288bf80f4bSopenharmony_ci    META_NS::IEvent::Token renderSizeHandlerToken_ {};
17298bf80f4bSopenharmony_ci    META_NS::IEvent::Token cameraHandlerToken_ {};
17308bf80f4bSopenharmony_ci    META_NS::IEvent::Token uriHandlerToken_ {};
17318bf80f4bSopenharmony_ci    META_NS::IEvent::Token rootNodeChangedToken_ {};
17328bf80f4bSopenharmony_ci    META_NS::IEvent::Token renderModeChangedToken_ {};
17338bf80f4bSopenharmony_ci    META_NS::IEvent::Token asyncChangedToken_ {};
17348bf80f4bSopenharmony_ci
17358bf80f4bSopenharmony_ci    SceneHolder::Ptr sceneHolder_;
17368bf80f4bSopenharmony_ci
17378bf80f4bSopenharmony_ci    BASE_NS::string rootNodeId_;
17388bf80f4bSopenharmony_ci    BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::INode::Ptr> nodes_;
17398bf80f4bSopenharmony_ci    BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::ICamera::WeakPtr> cameras_;
17408bf80f4bSopenharmony_ci    BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMesh::WeakPtr> meshes_;
17418bf80f4bSopenharmony_ci    BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMaterial::WeakPtr> materials_;
17428bf80f4bSopenharmony_ci    BASE_NS::unordered_map<BASE_NS::string, META_NS::IAnimation::WeakPtr> animations_;
17438bf80f4bSopenharmony_ci
17448bf80f4bSopenharmony_ci    BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMaterial::WeakPtr> uriMaterials_;
17458bf80f4bSopenharmony_ci
17468bf80f4bSopenharmony_ci    uint64_t instanceNumber_ { 0 };
17478bf80f4bSopenharmony_ci
17488bf80f4bSopenharmony_ci    // Preserve property instances while scene / ecs is invalid
17498bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr rootNodePtr_ {};
17508bf80f4bSopenharmony_ci    SCENE_NS::ICamera::Ptr cameraNodePtr_ {};
17518bf80f4bSopenharmony_ci    META_NS::IContent::Ptr contentImpl_ {};
17528bf80f4bSopenharmony_ci
17538bf80f4bSopenharmony_ci    uint64_t defaultCameraHandle_ { 0 };
17548bf80f4bSopenharmony_ci
17558bf80f4bSopenharmony_ci    // We need to add a node onto a container in bit awkward position (without exposing it to a public api)
17568bf80f4bSopenharmony_ci    // Store it here.
17578bf80f4bSopenharmony_ci    SCENE_NS::INode::Ptr currentParent_ {};
17588bf80f4bSopenharmony_ci    META_NS::IObjectHierarchyObserver::Ptr hierarchyController_;
17598bf80f4bSopenharmony_ci
17608bf80f4bSopenharmony_ci    META_NS::IAnimationController::Ptr animationController_;
17618bf80f4bSopenharmony_ci};
17628bf80f4bSopenharmony_ci
17638bf80f4bSopenharmony_ciBASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> SceneImpl::GetAnimations() const
17648bf80f4bSopenharmony_ci{
17658bf80f4bSopenharmony_ci    return animationController_->GetAnimations();
17668bf80f4bSopenharmony_ci}
17678bf80f4bSopenharmony_ciBASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> SceneImpl::GetRunning() const
17688bf80f4bSopenharmony_ci{
17698bf80f4bSopenharmony_ci    return animationController_->GetRunning();
17708bf80f4bSopenharmony_ci}
17718bf80f4bSopenharmony_cibool SceneImpl::AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation)
17728bf80f4bSopenharmony_ci{
17738bf80f4bSopenharmony_ci    return animationController_->AddAnimation(animation);
17748bf80f4bSopenharmony_ci}
17758bf80f4bSopenharmony_cibool SceneImpl::RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation)
17768bf80f4bSopenharmony_ci{
17778bf80f4bSopenharmony_ci    return animationController_->RemoveAnimation(animation);
17788bf80f4bSopenharmony_ci}
17798bf80f4bSopenharmony_civoid SceneImpl::Clear()
17808bf80f4bSopenharmony_ci{
17818bf80f4bSopenharmony_ci    animationController_->Clear();
17828bf80f4bSopenharmony_ci}
17838bf80f4bSopenharmony_ciMETA_NS::IAnimationController::StepInfo SceneImpl::Step(const META_NS::IClock::ConstPtr& clock)
17848bf80f4bSopenharmony_ci{
17858bf80f4bSopenharmony_ci    return animationController_->Step(clock);
17868bf80f4bSopenharmony_ci}
17878bf80f4bSopenharmony_ci
17888bf80f4bSopenharmony_ci} // namespace
17898bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
17908bf80f4bSopenharmony_civoid RegisterSceneImpl()
17918bf80f4bSopenharmony_ci{
17928bf80f4bSopenharmony_ci    META_NS::GetObjectRegistry().RegisterObjectType<SceneImpl>();
17938bf80f4bSopenharmony_ci}
17948bf80f4bSopenharmony_civoid UnregisterSceneImpl()
17958bf80f4bSopenharmony_ci{
17968bf80f4bSopenharmony_ci    META_NS::GetObjectRegistry().UnregisterObjectType<SceneImpl>();
17978bf80f4bSopenharmony_ci}
17988bf80f4bSopenharmony_ciSCENE_END_NAMESPACE()
1799