1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <algorithm>
16 #include <functional>
17 #include <scene_plugin/api/camera_uid.h>
18 #include <scene_plugin/api/environment_uid.h>
19 #include <scene_plugin/api/light_uid.h>
20 #include <scene_plugin/api/material_uid.h>
21 #include <scene_plugin/api/mesh_uid.h>
22 #include <scene_plugin/api/node_uid.h>
23 #include <scene_plugin/api/scene_uid.h>
24 #include <scene_plugin/api/view_node_uid.h>
25 #include <scene_plugin/interface/intf_ecs_scene.h>
26 #include <scene_plugin/interface/intf_environment.h>
27 
28 #include <3d/ecs/systems/intf_node_system.h>
29 #include <core/io/intf_file_manager.h>
30 
31 #include <meta/api/event_handler.h>
32 #include <meta/ext/object_container.h>
33 #include <meta/interface/animation/intf_animation_controller.h>
34 #include <meta/interface/intf_content.h>
35 #include <meta/interface/intf_named.h>
36 #include <meta/interface/intf_object_hierarchy_observer.h>
37 #include <meta/interface/serialization/intf_importer.h>
38 
39 #include "intf_node_private.h"
40 #include "intf_resource_private.h"
41 #include "scene_holder.h"
42 #include "task_utils.h"
43 
44 // "synchronously initialize the nodes from main scene file when the scene is loaded
45 
46 // Save the project .scene file when scene object is being serialized. In practice this may override the changes on
47 // scene file from other sources. To be rectified further
48 
49 // Name prefab instances with IObject::GetUid(). The other option is to use fixed "prefab_instance_NN" with running
50 // instance number. The latter may cause trouble with .scene-files if it contains previous instances
51 
52 using SCENE_NS::MakeTask;
53 namespace {
54 class NotifyOnExit {
55 public:
NotifyOnExit(bool notify,std::function<void ()> callback)56     explicit NotifyOnExit(bool notify, std::function<void()> callback) : notify_(notify), callback_(callback) {}
~NotifyOnExit()57     virtual ~NotifyOnExit()
58     {
59         if (notify_) {
60             callback_();
61         }
62     }
63     bool notify_;
64     std::function<void()> callback_;
65 };
66 
67 class SceneImpl final : public META_NS::ObjectContainerFwd<SceneImpl, SCENE_NS::ClassId::Scene, SCENE_NS::IScene,
68                             SCENE_NS::IEcsScene, META_NS::IContent, META_NS::IAnimationController> {
69     /// Add the animation controller stuff here. (as scene should be the controller of it's animations)
70     META_FORWARD_READONLY_PROPERTY(uint32_t, Count, animationController_->Count())
71     META_FORWARD_READONLY_PROPERTY(uint32_t, RunningCount, animationController_->RunningCount())
72 
73     BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> GetAnimations() const override;
74     BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> GetRunning() const override;
75     bool AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation) override;
76     bool RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation) override;
77     void Clear() override;
78     StepInfo Step(const META_NS::IClock::ConstPtr& clock) override;
79     // now back to normal scene impl
80 
81     META_IMPLEMENT_INTERFACE_PROPERTY(META_NS::INamed, BASE_NS::string, Name, {})
82     META_IMPLEMENT_INTERFACE_PROPERTY(
83         SCENE_NS::IScene, BASE_NS::string, SystemGraphUri, "project://assets/config/system_graph.json")
84     META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(
85         SCENE_NS::IScene, uint32_t, Status, SCENE_STATUS_UNINITIALIZED, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
86     META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IScene, SCENE_NS::INode::Ptr, RootNode, {})
87     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, SCENE_NS::ICamera::Ptr, DefaultCamera, {})
88     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, BASE_NS::string, Uri, {})
89     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, bool, Asynchronous, false)
90     META_IMPLEMENT_INTERFACE_ARRAY_PROPERTY(SCENE_NS::IScene, SCENE_NS::IMaterial::Ptr, Materials, {})
91     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, SCENE_NS::IRenderConfiguration::Ptr, RenderConfiguration, {})
92 
93     META_IMPLEMENT_EVENT(META_NS::IOnChanged, OnLoaded)
94     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IEcsScene, uint8_t, RenderMode, SCENE_NS::IEcsScene::RENDER_IF_DIRTY)
95 
96     META_FORWARD_READONLY_PROPERTY(IObject::Ptr, Content, (contentImpl_ ? contentImpl_->Content() : nullptr))
97     META_FORWARD_PROPERTY(bool, ContentSearchable, (contentImpl_ ? contentImpl_->ContentSearchable() : nullptr))
98     META_FORWARD_PROPERTY(
99         META_NS::IContentLoader::Ptr, ContentLoader, (contentImpl_ ? contentImpl_->ContentLoader() : nullptr))
100 
101     void SetRenderMode()
102     {
103         AddEngineTask(MakeTask(
__anon3577c0ce0202(auto sceneHolder) 104                           [renderMode = META_NS::GetValue(RenderMode())](auto sceneHolder) {
105                               sceneHolder->SetRenderMode(renderMode);
106                               return false;
107                           },
108                           sceneHolder_),
109             false);
110     }
111 
GetCameraHandle(const SCENE_NS::ICamera::Ptr & camera)112     uint64_t GetCameraHandle(const SCENE_NS::ICamera::Ptr& camera)
113     {
114         if (camera) {
115             auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(camera);
116             if (ecsObject) {
117                 return ecsObject->GetEntity().id;
118             }
119         }
120 
121         return SCENE_NS::IScene::DEFAULT_CAMERA;
122     }
123 
124     struct BitmapInfo {
125         BitmapInfo() = default;
BitmapInfo__anon3577c0ce0110::SceneImpl::BitmapInfo126         BitmapInfo(SCENE_NS::IBitmap::Ptr bmp) : bitmap(bmp) {}
127         BASE_NS::Math::UVec2 size {};
128         SCENE_NS::IBitmap::Ptr bitmap {};
129         META_NS::EventHandler bitmapChanged {};
130     };
131     BASE_NS::unordered_map<uint64_t, BitmapInfo> bitmaps_;
GetData(const SCENE_NS::ICamera::Ptr & camera)132     BitmapInfo& GetData(const SCENE_NS::ICamera::Ptr& camera)
133     {
134         auto cameraHandle = GetCameraHandle(camera);
135         if (cameraHandle == SCENE_NS::IScene::DEFAULT_CAMERA) {
136             cameraHandle = defaultCameraHandle_;
137         }
138         auto handle = cameraHandle;
139         if (cameraHandle == SCENE_NS::IScene::DEFAULT_CAMERA && !bitmaps_.empty()) {
140             handle = defaultCameraHandle_;
141         }
142         return bitmaps_[handle];
143     }
144 
SetBitmap(const SCENE_NS::IBitmap::Ptr & bitmap,const SCENE_NS::ICamera::Ptr & camera)145     void SetBitmap(const SCENE_NS::IBitmap::Ptr& bitmap, const SCENE_NS::ICamera::Ptr& camera) override
146     {
147         BitmapInfo& data = GetData(camera);
148         auto uiBitmap = interface_pointer_cast<SCENE_NS::IBitmap>(bitmap);
149         if (!uiBitmap) {
150             // disable bitmap override.
151             data.bitmapChanged.Unsubscribe();
152             data.bitmap = {};
153             sceneHolder_->SetCameraTarget(camera, data.size, {});
154             return;
155         }
156 
157         data.bitmap = uiBitmap;
158         data.size = data.bitmap->Size()->GetValue();
159         const auto rh = data.bitmap->GetRenderHandle();
160 
161         data.bitmapChanged.Subscribe(
162             uiBitmap->ResourceChanged(), META_NS::MakeCallback<META_NS::IOnChanged>([this, camera]() {
163                 BitmapInfo& data = GetData(camera);
164                 data.size = data.bitmap->Size()->GetValue();
165                 const auto rh = data.bitmap->GetRenderHandle();
166                 sceneHolder_->SetCameraTarget(camera, data.size, rh);
167             }));
168 
169         META_NS::Invoke<META_NS::IOnChanged>(uiBitmap->ResourceChanged());
170     }
171 
GetBitmap(bool notifyFrameDrawn,const SCENE_NS::ICamera::Ptr & camera)172     SCENE_NS::IBitmap::Ptr GetBitmap(bool notifyFrameDrawn, const SCENE_NS::ICamera::Ptr& camera) override
173     {
174         BitmapInfo& data = GetData(camera);
175         if (!data.bitmap) {
176             // there is no bitmap for this.
177             //  create it?
178         }
179         return data.bitmap;
180     }
181 
182 #define CREATE_META_INSTANCE(type, name) GetObjectRegistry().Create<META_NS::type>(META_NS::ClassId::name)
183 
184     // META_NS::IContent
SetContent(const META_NS::IObject::Ptr & content)185     bool SetContent(const META_NS::IObject::Ptr& content) override
186     {
187         // Should be no-op because the Root Node of a scene (content in this case) can be assigned only if the scene was
188         // reloaded.
189         return false;
190     }
191 
Build(const META_NS::IMetadata::Ptr & data)192     bool Build(const META_NS::IMetadata::Ptr& data) override
193     {
194         auto& registry = GetObjectRegistry();
195 
196         animationController_ = registry.Create<META_NS::IAnimationController>(META_NS::ClassId::AnimationController);
197 
198         using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
199         BASE_NS::shared_ptr<RENDER_NS::IRenderContext> rc;
200         META_NS::ITaskQueue::Ptr appQueue;
201         META_NS::ITaskQueue::Ptr engineQueue;
202 
203         if (data) {
204             if (auto prp = data->GetPropertyByName<IntfPtr>("RenderContext")) {
205                 rc = interface_pointer_cast<RENDER_NS::IRenderContext>(prp->GetValue());
206             }
207             if (auto prp = data->GetPropertyByName<IntfPtr>("EngineQueue")) {
208                 engineQueue = interface_pointer_cast<META_NS::ITaskQueue>(prp->GetValue());
209             }
210             if (auto prp = data->GetPropertyByName<IntfPtr>("AppQueue")) {
211                 appQueue = interface_pointer_cast<META_NS::ITaskQueue>(prp->GetValue());
212             }
213         }
214         if ((!rc) || (!engineQueue) || (!appQueue)) {
215             return false;
216         }
217 
218         hierarchyController_ =
219             registry.Create<META_NS::IObjectHierarchyObserver>(SCENE_NS::ClassId::NodeHierarchyController);
220 
221         contentImpl_ = registry.Create<META_NS::IContent>(META_NS::ClassId::ContentObject);
222         if (const auto req = interface_cast<META_NS::IRequiredInterfaces>(contentImpl_)) {
223             req->SetRequiredInterfaces({ SCENE_NS::INode::UID });
224         }
225 
226         sceneHolder_.reset(new SceneHolder(GetInstanceId(), registry, rc, appQueue, engineQueue));
227         sceneHolder_->SetOperationMode(Asynchronous()->GetValue());
228 
229         asyncChangedToken_ = Asynchronous()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(
230             [](const auto& sceneHolder, const auto& async) {
231                 if (sceneHolder && async) {
232                     sceneHolder->SetOperationMode(async->GetValue());
233                 }
234             },
235             sceneHolder_, Asynchronous()));
236 
237         renderModeChangedToken_ = RenderMode()->OnChanged()->AddHandler(
238             META_NS::MakeCallback<META_NS::IOnChanged>([weak = BASE_NS::weak_ptr(GetSelf())]() {
239                 if (auto self = static_pointer_cast<SceneImpl>(weak.lock())) {
240                     self->SetRenderMode();
241                 }
242             }));
243 
244         sceneHolder_->SetInitializeCallback(
245             META_NS::MakeCallback<SceneHolder::ISceneInitialized>(
246                 [me = BASE_NS::weak_ptr(GetSelf())](const BASE_NS::string& rootId, const BASE_NS::string& cameraId) {
247                     if (auto self = me.lock().get())
248                         static_cast<SceneImpl*>(self)->onSceneInitialized(rootId, cameraId);
249                 }),
250             sceneHolder_);
251         sceneHolder_->SetSceneLoadedCallback(
252             META_NS::MakeCallback<SceneHolder::ISceneLoaded>([me = BASE_NS::weak_ptr(GetSelf())](uint32_t status) {
253                 if (auto self = me.lock().get())
254                     static_cast<SceneImpl*>(self)->OnSceneLoaded(status);
255             }),
256             sceneHolder_);
257 
258         sceneHolder_->Initialize(sceneHolder_);
259         sceneHolder_->SetSystemGraphUri(META_NS::GetValue(SystemGraphUri()));
260         SubscribeToPropertyChanges();
261 
262         return true;
263     }
264 
265     // this is not usually needed as explicit action, but the scene will pickup changes from
266     // ScenePresenters on the fly
OnCameraChanged()267     void OnCameraChanged()
268     {
269         // Async, sceneholder takes over
270         if (sceneHolder_) {
271             sceneHolder_->ChangeCamera(META_ACCESS_PROPERTY(DefaultCamera)->GetValue());
272         }
273     }
274 
275     // Async, sceneholder takes over
276     BASE_NS::vector<META_NS::IAnimation::Ptr> allAnims_;
GetAnimations()277     BASE_NS::vector<META_NS::IAnimation::Ptr> GetAnimations() override
278     {
279         // adds/removes animations to cache..
280         auto tmp = allAnims_;
281         allAnims_.clear();
282         for (auto anim : sceneHolder_->GetAnimations()) {
283             if (auto i = interface_cast<META_NS::IObject>(anim)) {
284                 auto name = i->GetName();
285 
286                 size_t ix = 0;
287                 if (!RollOverPrefix(ix, name, ANIMATIONS_PREFIX)) {
288                     continue;
289                 }
290 
291                 // check cache
292                 META_NS::IAnimation::Ptr rn;
293                 auto subname = name.substr(ix);
294                 for (auto& a : tmp) {
295                     if (interface_cast<META_NS::IObject>(a)->GetName() == subname) {
296                         rn = a;
297                         break;
298                     }
299                 }
300                 if (!rn) {
301                     rn = interface_pointer_cast<META_NS::IAnimation>(
302                         CreateNode(name.substr(ix), false, SCENE_NS::ClassId::Animation.Id(),
303                             SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
304                 }
305 
306                 allAnims_.push_back(rn);
307             }
308         }
309         return allAnims_;
310     }
311 
GetAnimation(const BASE_NS::string_view name)312     META_NS::IAnimation::Ptr GetAnimation(const BASE_NS::string_view name) override
313     {
314 #ifndef USE_DIRECT_ECS_ANIMATION
315 
316         size_t ix = 0;
317         if (!RollOverPrefix(ix, name, ANIMATIONS_PREFIX)) {
318             return { nullptr };
319         }
320 
321         // check cache
322         if (auto it = animations_.find(name.substr(ix)); it != animations_.end()) {
323             if (auto animation = it->second.lock()) {
324                 return animation;
325             }
326         }
327         // Create EcsObject. When running asynchronously, we have no way of knowing if we can rely that existing
328         // animation will be found
329         auto anim = interface_pointer_cast<META_NS::IAnimation>(CreateNode(name.substr(ix), false,
330             SCENE_NS::ClassId::Animation.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
331 
332         return anim;
333 #else
334         auto ecsAnimation = sceneHolder_->GetAnimation(BASE_NS::string(name.data(), name.size()));
335 
336         if (!ecsAnimation) {
337             ecsAnimation = GetObjectRegistry().Create<SCENE_NS::IEcsAnimation>(SCENE_NS::ClassId::EcsAnimation);
338             AddEngineTask(
339                 META_NS::MakeCallable<META_NS::ITaskQueueTask>(
340                     [ecsAnimation, nameString = BASE_NS::string(name.data(), name.size()),
341                         weak = BASE_NS::weak_ptr(sceneHolder_)]() {
342                         if (auto sceneHolder = weak.lock()) {
343                             CORE_NS::Entity entity;
344                             if (sceneHolder->FindAnimation(nameString, entity)) {
345                                 if (auto ecsProxyIf = interface_pointer_cast<SCENE_NS::IEcsProxyObject>(ecsAnimation)) {
346                                     ecsProxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
347                                 }
348                                 ecsAnimation->SetEntity(*sceneHolder->GetEcs(), entity);
349                             }
350                         }
351                         return false;
352                     }),
353                 false);
354         }
355         META_ACCESS_PROPERTY(Animations)->Get()->Add(ecsAnimation);
356 
357         return interface_pointer_cast<META_NS::IAnimation>(ecsAnimation);
358 #endif
359     }
360 
CreateEmpty()361     void CreateEmpty() override
362     {
363         Load("scene://empty");
364     }
365 
Load(const BASE_NS::string_view uri)366     bool Load(const BASE_NS::string_view uri) override
367     {
368         if (!Name()->IsValueSet()) {
369             SetValue(Name(), "Scene");
370         }
371 
372         if (!uri.empty()) {
373             META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_LOADING);
374             sceneHolder_->Load(BASE_NS::string(uri.data(), uri.size()));
375             return true;
376         }
377         return false;
378     }
379 
onSystemGraphUriChanged()380     void onSystemGraphUriChanged()
381     {
382         META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_LOADING);
383         sceneHolder_->SetSystemGraphUri(META_NS::GetValue(SystemGraphUri()));
384     }
385 
SetRenderSize(uint32_t width,uint32_t height,const SCENE_NS::ICamera::Ptr & camera)386     void SetRenderSize(uint32_t width, uint32_t height, const SCENE_NS::ICamera::Ptr& camera) override
387     {
388         if (camera) {
389             camera->SetDefaultRenderTargetSize(width, height);
390         } else if (auto defaultCamera = META_NS::GetValue(DefaultCamera())) {
391             defaultCamera->SetDefaultRenderTargetSize(width, height);
392         }
393     }
394 
GetNode(const BASE_NS::string_view path,const BASE_NS::Uid classId,SCENE_NS::INode::BuildBehavior buildBehavior)395     SCENE_NS::INode::Ptr GetNode(const BASE_NS::string_view path, const BASE_NS::Uid classId,
396         SCENE_NS::INode::BuildBehavior buildBehavior) override
397     {
398         return GetNodeRecursive(path, classId, true, buildBehavior);
399     }
400 
ResolveNodeTypeFromPath(const BASE_NS::string_view patchedPath,bool isNodeType)401     META_NS::ObjectId ResolveNodeTypeFromPath(const BASE_NS::string_view patchedPath, bool isNodeType)
402     {
403         // This is best effort
404         // We cannot determine the type unless ECS has probed the component
405 
406         // This kind of introspection may cause materials and meshes to be treated as nodes
407         // which kind of contradicts with their normal use through API
408         auto ecs = GetEcs();
409         CORE3D_NS::INodeSystem& nodeSystem = *CORE_NS::GetSystem<CORE3D_NS::INodeSystem>(*ecs);
410         const auto& root = nodeSystem.GetRootNode();
411 
412         const auto& ecsNode = root.LookupNodeByPath(patchedPath);
413         CORE_NS::Entity entity {};
414         if (ecsNode) {
415             entity = ecsNode->GetEntity();
416         } else {
417             CORE_LOG_W("%s:No entity for %s, type info not available", __func__, BASE_NS::string(patchedPath).c_str());
418             CORE_LOG_W("If you know the expected type, consider using a template or providing the class id");
419         }
420 
421         // shadow camera could provide false positive, so order matters
422         if (auto lightManager = ecs->GetComponentManager(CORE3D_NS::ILightComponentManager::UID)) {
423             if (lightManager->HasComponent(entity)) {
424                 return SCENE_NS::ClassId::Light;
425             }
426         }
427 
428         if (auto cameraManager = ecs->GetComponentManager(CORE3D_NS::ICameraComponentManager::UID)) {
429             if (cameraManager->HasComponent(entity)) {
430                 return SCENE_NS::ClassId::Camera;
431             }
432         }
433 
434         if (auto envManager = ecs->GetComponentManager(CORE3D_NS::IEnvironmentComponentManager::UID)) {
435             if (envManager->HasComponent(entity)) {
436                 return SCENE_NS::ClassId::Environment;
437             }
438         }
439 
440         if (!isNodeType) {
441             if (auto nodeManager = ecs->GetComponentManager(CORE3D_NS::INodeComponentManager::UID)) {
442                 // quirk, prefer nodes with node component treated as node)
443                 if (!nodeManager->HasComponent(entity)) {
444                     if (auto meshManager = ecs->GetComponentManager(CORE3D_NS::IMeshComponentManager::UID)) {
445                         if (meshManager->HasComponent(entity)) {
446                             return SCENE_NS::ClassId::Mesh;
447                         }
448                     }
449                     if (auto materialManager = ecs->GetComponentManager(CORE3D_NS::IMaterialComponentManager::UID)) {
450                         if (materialManager->HasComponent(entity)) {
451                             return SCENE_NS::ClassId::Material;
452                         }
453                     }
454                 }
455             }
456         }
457 
458         return SCENE_NS::ClassId::Node;
459     }
460 
GetNodeRecursive(const BASE_NS::string_view path,const META_NS::ObjectId classId,bool recurse,SCENE_NS::INode::BuildBehavior buildBehavior)461     SCENE_NS::INode::Ptr GetNodeRecursive(const BASE_NS::string_view path, const META_NS::ObjectId classId,
462         bool recurse, SCENE_NS::INode::BuildBehavior buildBehavior)
463     {
464         if (path.empty() || path == "/") {
465             return SCENE_NS::INode::Ptr {};
466         }
467 
468         BASE_NS::string patchedPath = recurse ? NormalizePath(path) : BASE_NS::string(path.data(), path.size());
469         if (auto ite = nodes_.find(patchedPath) != nodes_.cend()) {
470             return nodes_[patchedPath];
471         }
472 
473         // ensure parent objects exist
474         if (recurse) {
475             size_t ix = patchedPath.find('/', 1);
476             while (BASE_NS::string_view::npos != ix) {
477                 auto substr = patchedPath.substr(0, ix);
478                 // When we traverse up the tree, we must ensure that the object is a node.
479                 currentParent_ = GetNodeRecursive(substr, SCENE_NS::INode::UID, false, buildBehavior);
480                 ++ix;
481                 ix = patchedPath.find('/', ix);
482             }
483         }
484 
485         auto ecs = GetEcs();
486         META_NS::ObjectId implementationId = SCENE_NS::ClassId::Node;
487 
488         bool isNodeType = (classId == SCENE_NS::INode::UID);
489         if ((classId == META_NS::IObject::UID || isNodeType) && ecs) {
490             implementationId = ResolveNodeTypeFromPath(patchedPath, isNodeType);
491 
492         } else {
493             implementationId = classId;
494         }
495 
496         auto node = CreateNode(patchedPath, false, implementationId, buildBehavior);
497 
498         currentParent_ = {};
499         return node; // finalInterface;
500     }
501 
RemoveNodeFromCurrentContainer(SCENE_NS::INode::Ptr & node)502     void RemoveNodeFromCurrentContainer(SCENE_NS::INode::Ptr& node)
503     {
504         if (auto containable = interface_cast<META_NS::IContainable>(node)) {
505             if (auto parent = interface_pointer_cast<META_NS::IContainer>(containable->GetParent())) {
506                 parent->Remove(node);
507             }
508         }
509     }
510 
RollOverPrefix(size_t & ix,const BASE_NS::string_view & name,const BASE_NS::string_view & prefix)511     bool RollOverPrefix(size_t& ix, const BASE_NS::string_view& name, const BASE_NS::string_view& prefix)
512     {
513         while (ix < name.length() && name[ix] == '/') {
514             ix++;
515         }
516 
517         if (name.substr(ix).find(prefix) == 0) {
518             ix += prefix.length();
519         }
520 
521         while (ix < name.length() && name[ix] == '/') {
522             ix++;
523         }
524 
525         return ix < name.length();
526     }
527 
AddMaterial(SCENE_NS::IMaterial::Ptr material)528     void AddMaterial(SCENE_NS::IMaterial::Ptr material) override
529     {
530         auto materials = Materials()->GetValue();
531         auto it = std::find(materials.begin(), materials.end(), material);
532         if (it != materials.end()) {
533             // Already exists.
534             CORE_LOG_D("Trying to add same material to scene multiple times.");
535             return;
536         }
537 
538         Materials()->AddValue(material);
539         UpdateCachedReference(interface_pointer_cast<SCENE_NS::INode>(material));
540     }
541 
RemoveMaterial(SCENE_NS::IMaterial::Ptr material)542     void RemoveMaterial(SCENE_NS::IMaterial::Ptr material) override
543     {
544         auto lock = Materials().GetLockedAccess();
545         auto vec = lock->GetValue();
546         for (size_t index = 0; index != vec.size(); ++index) {
547             if (vec[index] == material) {
548                 lock->RemoveAt(index);
549                 break;
550             }
551         }
552 
553         for (auto&& ite : materials_) {
554             if (ite.second.lock() == material) {
555                 ReleaseMaterial(ite.first);
556                 break;
557             }
558         }
559     }
560 
GetCameras() const561     BASE_NS::vector<SCENE_NS::ICamera::Ptr> GetCameras() const override
562     {
563         BASE_NS::vector<SCENE_NS::ICamera::Ptr> result;
564         for (auto c : cameras_) {
565             if (auto cam = c.second.lock()) {
566                 result.push_back(cam);
567             }
568         }
569         return result;
570     }
571 
GetMaterials() const572     BASE_NS::vector<SCENE_NS::IMaterial::Ptr> GetMaterials() const override
573     {
574         BASE_NS::vector<SCENE_NS::IMaterial::Ptr> result;
575         for (auto& material : materials_) {
576             auto ptr = material.second.lock();
577             if (ptr) {
578                 result.push_back(ptr);
579             }
580         }
581 
582         return result;
583     }
584 
585     // Returns a material from the scene with a given path
GetMaterial(const BASE_NS::string_view name)586     SCENE_NS::IMaterial::Ptr GetMaterial(const BASE_NS::string_view name) override
587     {
588         // The material file, aka uri-path is somewhat parallel due ownership is different for uri-materials
589         // however, one can claim traditional handle and have them preserved
590         // through flat cache. Should be consolidated someday.
591         if (name.find("://") != BASE_NS::string_view::npos) {
592             return GetOrLoadMaterial(name);
593         }
594 
595         size_t ix = 0;
596         if (!RollOverPrefix(ix, name, MATERIALS_PREFIX)) {
597             return { nullptr };
598         }
599 
600         // check cache (first with name:entityid)
601         if (auto it = materials_.find(name.substr(ix)); it != materials_.end()) {
602             if (auto material = it->second.lock()) {
603                 return material;
604             }
605         }
606 
607         // check cache (direct material name)
608         for (auto entry : materials_) {
609             auto material = entry.second.lock();
610             if (auto node = interface_pointer_cast<SCENE_NS::INode>(material)) {
611                 if (node->Name()->GetValue() == name) {
612                     return material;
613                 }
614             }
615         }
616 
617         if (auto it = materials_.find(name.substr(ix)); it != materials_.end()) {
618             if (auto material = it->second.lock()) {
619                 return material;
620             }
621         }
622 
623         // Create EcsObject. When running asynchronously, we have no way of knowing if we should create
624         // new node or just rely that existing will be found
625         auto mat = interface_pointer_cast<SCENE_NS::IMaterial>(CreateNode(name.substr(ix), false,
626             SCENE_NS::ClassId::Material.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
627 
628         return mat;
629     }
630 
GetMeshes() const631     BASE_NS::vector<SCENE_NS::IMesh::Ptr> GetMeshes() const override
632     {
633         BASE_NS::vector<SCENE_NS::IMesh::Ptr> result;
634         for (auto mesh : meshes_) {
635             auto ptr = mesh.second.lock();
636             if (ptr) {
637                 result.push_back(ptr);
638             }
639         }
640 
641         return result;
642     }
643 
644     // Returns a material from the scene with a given name
GetMesh(const BASE_NS::string_view name)645     SCENE_NS::IMesh::Ptr GetMesh(const BASE_NS::string_view name) override
646     {
647         size_t ix = 0;
648         if (!RollOverPrefix(ix, name, MESHES_PREFIX)) {
649             return { nullptr };
650         }
651 
652         // check cache
653         if (auto it = meshes_.find(name.substr(ix)); it != meshes_.end()) {
654             if (auto mesh = it->second.lock()) {
655                 return mesh;
656             }
657         }
658         // Create EcsObject. When running asynchronously, we have no way of knowing if we should create
659         // new node or just rely that existing will be found
660         auto mesh = interface_pointer_cast<SCENE_NS::IMesh>(CreateNode(name.substr(ix), false,
661             SCENE_NS::ClassId::Mesh.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
662 
663         return mesh;
664     }
665 
constructPath(CORE_NS::Entity ent) const666     BASE_NS::string constructPath(CORE_NS::Entity ent) const
667     {
668         auto ecs = GetEcs();
669         auto* nodeManager = CORE_NS::GetManager<CORE3D_NS::INodeComponentManager>(*ecs);
670         auto* nameManager = CORE_NS::GetManager<CORE3D_NS::INameComponentManager>(*ecs);
671         BASE_NS::string path;
672         if (nodeManager && nameManager) {
673             auto curent = ent;
674             for (;;) {
675                 if (!CORE_NS::EntityUtil::IsValid(curent)) {
676                     // Reached root.
677                     break;
678                 }
679                 if (!nodeManager->HasComponent(curent)) {
680                     // not a node?
681                     return "";
682                 }
683                 if (!nameManager->HasComponent(curent)) {
684                     // no name in hierarchy. "fail"? or generate "name" for path..
685                     return "";
686                 }
687                 auto namecomp = nameManager->Get(curent);
688                 if (!path.empty()) {
689                     path.insert(0, "/");
690                 }
691                 path.insert(0, namecomp.name.c_str());
692                 const auto& node = nodeManager->Get(curent);
693                 curent = node.parent;
694             }
695         }
696         if (!path.empty()) {
697             path.insert(0, "/");
698         }
699         return path;
700     }
701     // Implementation for scene change callbacks.
702     // The tricky bit here is that we may have placeholders on containers that may have received serialized data
703     // during construction, so we just cannot replace those, but need to rebind them instead
onSceneInitialized(const BASE_NS::string & rootId,const BASE_NS::string & cameraId)704     void onSceneInitialized(const BASE_NS::string& rootId, const BASE_NS::string& cameraId)
705     {
706         // Set root node
707         rootNodeId_ = rootId;
708         auto rootIdNormalized = NormalizePath(rootNodeId_);
709 
710         if (rootNodePtr_) {
711             // check if someone assigned us a root node while we are preserving the previous one
712             if (!META_ACCESS_PROPERTY(RootNode)->GetValue()) {
713                 META_ACCESS_PROPERTY(RootNode)->SetValue(rootNodePtr_);
714             }
715             rootNodePtr_.reset();
716         }
717 
718         if (cameraNodePtr_) {
719             // check if someone assigned us a root node while we are preserving the previous one
720             if (!META_ACCESS_PROPERTY(DefaultCamera)->GetValue()) {
721                 META_ACCESS_PROPERTY(DefaultCamera)->SetValue(cameraNodePtr_);
722             }
723             cameraNodePtr_.reset();
724         }
725 
726         if (auto renderConfiguration = META_NS::GetValue(RenderConfiguration())) {
727             // Ensure the render configuration is bound to scene.
728             auto resource = interface_pointer_cast<IResourcePrivate>(renderConfiguration);
729             if (resource) {
730                 resource->Connect(sceneHolder_);
731             }
732         }
733 
734         // setup subscription for toolkit changes
735         RenderConfiguration()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
736             auto resource = interface_pointer_cast<IResourcePrivate>(GetValue(RenderConfiguration()));
737             if (resource) {
738                 resource->Connect(sceneHolder_);
739             }
740         }),
741             reinterpret_cast<uint64_t>(this));
742 
743         if (auto rootNode = META_ACCESS_PROPERTY(RootNode)->GetValue()) {
744             // switch new ecsObject
745             BindNodeToEcs(rootNode, rootIdNormalized, false);
746             nodes_[rootIdNormalized] = rootNode;
747         } else {
748             META_ACCESS_PROPERTY(RootNode)->SetValue(GetSelf<SCENE_NS::IScene>()->GetNode<SCENE_NS::INode>(rootId));
749         }
750 
751         auto defaultCamera = META_ACCESS_PROPERTY(DefaultCamera)->GetValue();
752         if (!defaultCamera) {
753             defaultCamera = GetSelf<SCENE_NS::IScene>()->GetNode<SCENE_NS::ICamera>(cameraId);
754             META_ACCESS_PROPERTY(DefaultCamera)->SetValue(defaultCamera);
755         }
756 
757         if (defaultCamera) {
758             ActivateCamera(defaultCamera);
759         }
760 
761         // under normal conditions, this would take place asyncronously
762         // however, if we are running on synchronous mode we need to restore the ecs bindings here
763         for (auto& prevNode : nodes_) {
764             if (prevNode.first != rootIdNormalized && prevNode.first != cameraId) {
765                 BindNodeToEcs(prevNode.second, prevNode.first, false);
766             }
767         }
768 
769         // This is information only unless we instantiate all the scene nodes immediately
770         // Should be flagged more effectively on production builds
771         CORE3D_NS::INodeSystem& nodeSystem = *CORE_NS::GetSystem<CORE3D_NS::INodeSystem>(*GetEcs());
772         const auto& root = nodeSystem.GetRootNode();
773 
774         instantiateNodes(root, "/");
775 
776         // notify observers about the change
777         META_ACCESS_PROPERTY(Status)->SetValue(SCENE_NS::IScene::SCENE_STATUS_READY);
778         META_NS::Invoke<META_NS::IOnChanged>(OnLoaded());
779 
780         sceneHolder_->SetUninitializeCallback(
781             META_NS::MakeCallback<SceneHolder::ISceneUninitialized>([me = BASE_NS::weak_ptr(GetSelf())]() {
782                 if (auto self = me.lock().get())
783                     static_cast<SceneImpl*>(self)->DetachScene();
784             }),
785             sceneHolder_);
786     }
787 
OnSceneLoaded(uint32_t status)788     void OnSceneLoaded(uint32_t status)
789     {
790         META_ACCESS_PROPERTY(Status)->SetValue(status);
791         if (status == SCENE_NS::IScene::SCENE_STATUS_READY) {
792             META_NS::Invoke<META_NS::IOnChanged>(OnLoaded());
793         }
794     }
795 
796     // Implementation for scene processing.
797 
DetachScene(bool setDirty=false)798     void DetachScene(bool setDirty = false)
799     {
800         if (META_ACCESS_PROPERTY(Status)) {
801             META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_UNINITIALIZED);
802         }
803 
804         cameras_.clear();
805         materials_.clear();
806         meshes_.clear();
807         animations_.clear();
808         nodes_.clear();
809 
810         // This effectively means that we keep the scene user objects forcibly live
811         // until we get fresh ones to replace them, not sure if that is intended
812         rootNodePtr_ = META_ACCESS_PROPERTY(RootNode)->GetValue();
813         cameraNodePtr_ = META_ACCESS_PROPERTY(DefaultCamera)->GetValue();
814         META_ACCESS_PROPERTY(RootNode)->SetValue({});
815         META_ACCESS_PROPERTY(DefaultCamera)->SetValue({});
816 
817         if (setDirty) {
818             for (const auto& bitmap : bitmaps_) {
819                 /*auto externalBitmap = interface_pointer_cast<UI_NS::ILume2DExternalBitmap>(bitmap.second.bitmap);
820                 if (externalBitmap) {
821                     externalBitmap->SetCoreBitmap(nullptr);
822                 }*/
823                 bitmap.second.bitmap->SetRenderHandle({}, { 0, 0 });
824             }
825         }
826     }
827 
instantiateNodes(const CORE3D_NS::ISceneNode & node,BASE_NS::string path)828     void instantiateNodes(const CORE3D_NS::ISceneNode& node, BASE_NS::string path)
829     {
830         if (!path.empty() && path.back() != '/') {
831             path.append("/");
832         }
833         if (!node.GetName().empty()) {
834             path.append(node.GetName());
835         } else {
836 #ifndef INSTANTIATE_NODES_ON_INITIALIZE
837             path.append("[");
838             path.append(BASE_NS::to_string(node.GetEntity().id));
839             path.append("]");
840 #endif
841         }
842         SCENE_PLUGIN_VERBOSE_LOG("%s", path.c_str());
843 #ifdef INSTANTIATE_NODES_ON_INITIALIZE
844         GetNode(path);
845 #endif
846 
847         for (const auto child : node.GetChildren())
848             instantiateNodes(*child, path);
849     }
850 
NormalizePath(const BASE_NS::string_view & path)851     BASE_NS::string NormalizePath(const BASE_NS::string_view& path)
852     {
853         BASE_NS::string patchedPath;
854         if (path == rootNodeId_) {
855             patchedPath = path;
856             patchedPath.insert(0, "/");
857         } else {
858             bool hasSlash = (path[0] == '/');
859             if (path.compare(hasSlash ? 1 : 0, rootNodeId_.size(), rootNodeId_.data())) {
860                 SCENE_PLUGIN_VERBOSE_LOG("%s: the path does not contain root node, patching the path", __func__);
861                 patchedPath.append("/");
862                 patchedPath.append(rootNodeId_);
863                 if (!hasSlash) {
864                     patchedPath.append("/");
865                 }
866                 patchedPath.append(path.data(), path.size());
867             } else {
868                 patchedPath = path;
869                 if (!hasSlash) {
870                     patchedPath = "/" + patchedPath;
871                 }
872             }
873         }
874         return patchedPath;
875     }
876 
FindEcsObject(const BASE_NS::string_view & path,BASE_NS::string & patchedPath)877     SCENE_NS::IEcsObject::Ptr FindEcsObject(const BASE_NS::string_view& path, BASE_NS::string& patchedPath)
878     {
879         patchedPath = NormalizePath(path);
880 
881         if (auto it = nodes_.find(patchedPath); it != nodes_.end()) {
882             return interface_pointer_cast<SCENE_NS::IEcsObject>(it->second);
883         }
884         return SCENE_NS::IEcsObject::Ptr {};
885     }
886 
CreateNewEcsObject(const BASE_NS::string &)887     SCENE_NS::IEcsObject::Ptr CreateNewEcsObject(const BASE_NS::string& /*path*/)
888     {
889         // Create new helper object
890         // Not much left here, could be presumably removed
891         auto ret = SCENE_NS::IEcsObject::Ptr {};
892 
893 // Allow construction to progress even ecs was not available,
894 // nodes will perform initialization asynchronously anyway
895         if (auto object = META_NS::GetObjectRegistry().Create(SCENE_NS::ClassId::EcsObject)) {
896             ret = interface_pointer_cast<SCENE_NS::IEcsObject>(object);
897         }
898         return ret;
899     }
900 
GetEcsObject(const BASE_NS::string_view & path)901     SCENE_NS::IEcsObject::Ptr GetEcsObject(const BASE_NS::string_view& path) override
902     {
903         return interface_pointer_cast<SCENE_NS::IEcsObject>(
904             GetNode(path, META_NS::IObject::UID, SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
905     }
906 
GetEcs()907     CORE_NS::IEcs::Ptr GetEcs() override
908     {
909         return sceneHolder_->GetEcs();
910     }
911 
GetEcs() const912     CORE_NS::IEcs::Ptr GetEcs() const
913     {
914         return sceneHolder_->GetEcs();
915     }
916 
AddEngineTask(const META_NS::ITaskQueue::CallableType::Ptr & task,bool runDeferred)917     META_NS::ITaskQueue::Token AddEngineTask(
918         const META_NS::ITaskQueue::CallableType::Ptr& task, bool runDeferred) override
919     {
920         if (sceneHolder_) {
921             return sceneHolder_->QueueEngineTask(task, runDeferred);
922         }
923         return META_NS::ITaskQueue::Token {};
924     }
925 
AddApplicationTask(const META_NS::ITaskQueue::CallableType::Ptr & task,bool runDeferred)926     META_NS::ITaskQueue::Token AddApplicationTask(
927         const META_NS::ITaskQueue::CallableType::Ptr& task, bool runDeferred) override
928     {
929         if (sceneHolder_) {
930             return sceneHolder_->QueueApplicationTask(task, runDeferred);
931         }
932         return META_NS::ITaskQueue::Token {};
933     }
934 
CancelEngineTask(META_NS::ITaskQueue::Token token)935     void CancelEngineTask(META_NS::ITaskQueue::Token token) override
936     {
937         if (sceneHolder_) {
938             return sceneHolder_->CancelEngineTask(token);
939         }
940     }
941 
CancelAppTask(META_NS::ITaskQueue::Token token)942     void CancelAppTask(META_NS::ITaskQueue::Token token) override
943     {
944         if (sceneHolder_) {
945             return sceneHolder_->CancelAppTask(token);
946         }
947     }
948 
GetEntityCollection()949     SCENE_NS::IEntityCollection* GetEntityCollection() override
950     {
951         if (sceneHolder_) {
952             return sceneHolder_->GetEntityCollection();
953         }
954 
955         return {};
956     }
957 
GetAssetManager()958     SCENE_NS::IAssetManager* GetAssetManager() override
959     {
960         if (sceneHolder_) {
961             return sceneHolder_->GetAssetManager();
962         }
963 
964         return {};
965     }
966 
GetOrLoadMaterial(BASE_NS::string_view uri)967     SCENE_NS::IMaterial::Ptr GetOrLoadMaterial(BASE_NS::string_view uri)
968     {
969         // check cache
970         if (auto it = uriMaterials_.find(uri); it != uriMaterials_.end()) {
971             if (auto material = it->second.lock()) {
972                 return material;
973             }
974         }
975 
976         auto ret = LoadMaterial(uri);
977         uriMaterials_[uri] = ret;
978         return ret;
979     }
980 
LoadMaterial(BASE_NS::string_view uri)981     SCENE_NS::IMaterial::Ptr LoadMaterial(BASE_NS::string_view uri) override
982     {
983         bool isGltfUri = (uri.find(".glb/materials/") != BASE_NS::string_view::npos) ||
984                          (uri.find(".gltf/materials/") != BASE_NS::string_view::npos);
985         if (isGltfUri) {
986             auto resource = CreateResourceFromUri(SCENE_NS::ClassId::Material, uri);
987             if (resource) {
988                 return interface_pointer_cast<SCENE_NS::IMaterial>(resource);
989             }
990 
991             CORE_LOG_W("Could not resolve material URI: %s", BASE_NS::string(uri.data(), uri.size()).c_str());
992             // we could return the control to CreateMaterial and let the engine resolve the materials with uri
993             // componenta as they seem to be in place when loaded from glb or gltf
994         }
995 
996         SCENE_NS::IMaterial::Ptr ret;
997 
998         return ret;
999     }
1000 
CreateNode(const META_NS::ObjectId classId)1001     SCENE_NS::INode::Ptr CreateNode(const META_NS::ObjectId classId)
1002     {
1003         SCENE_NS::INode::Ptr node;
1004 
1005         if (classId == SCENE_NS::ClassId::Light.Id() || classId == SCENE_NS::ILight::UID) {
1006             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Light);
1007         } else if (classId == SCENE_NS::ClassId::Camera.Id() || classId == SCENE_NS::ICamera::UID) {
1008             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Camera);
1009         } /*else if (classId == SCENE_NS::ClassId::ViewNode.Id() || classId == SCENE_NS::IViewNode::UID) {
1010             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::ViewNode);
1011         } */
1012         else if (classId == SCENE_NS::ClassId::Material.Id() || classId == SCENE_NS::IMaterial::UID) {
1013             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Material);
1014         } else if (classId == SCENE_NS::ClassId::Mesh.Id() || classId == SCENE_NS::IMesh::UID) {
1015             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Mesh);
1016         } else if (classId == SCENE_NS::ClassId::Animation.Id() || classId == META_NS::IAnimation::UID) {
1017             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Animation);
1018         } else if (classId == SCENE_NS::ClassId::Environment.Id() || classId == SCENE_NS::IEnvironment::UID) {
1019             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Environment);
1020         } else {
1021             if (classId != META_NS::IObject::UID && classId != SCENE_NS::ClassId::Node.Id() &&
1022                 classId != SCENE_NS::INode::UID) {
1023                 CORE_LOG_W("%s: uid not known, returning INode instance", __func__);
1024             }
1025 
1026             node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Node);
1027         }
1028 
1029         return node;
1030     }
1031 
1032     // Quirk / constraints:
1033     // 1) has name -> cached and asynchronously initialized
1034     // 2) name empty, just an object, not sure if needed at the end
CreateNode(const BASE_NS::string_view name,bool createEngineObject,const META_NS::ObjectId classId,SCENE_NS::INode::BuildBehavior buildBehavior)1035     SCENE_NS::INode::Ptr CreateNode(const BASE_NS::string_view name, bool createEngineObject,
1036         const META_NS::ObjectId classId, SCENE_NS::INode::BuildBehavior buildBehavior) override
1037     {
1038         if (const auto& ite = nodes_.find(name); ite != nodes_.cend() && createEngineObject) {
1039             CORE_LOG_W("Refusing to create new duplicate node: %s", BASE_NS::string(name.data(), name.size()).c_str());
1040             return ite->second;
1041         }
1042 
1043         SCENE_NS::INode::Ptr node = CreateNode(classId);
1044 
1045         if (node) {
1046             node->BuildChildren(buildBehavior);
1047             BindNodeToEcs(node, name, createEngineObject);
1048         }
1049         return node;
1050     }
1051 
CreateResourceFromUri(const META_NS::ObjectId classId,BASE_NS::string_view uri)1052     SCENE_NS::INode::Ptr CreateResourceFromUri(const META_NS::ObjectId classId, BASE_NS::string_view uri)
1053     {
1054         SCENE_NS::INode::Ptr node;
1055 
1056         auto entity = sceneHolder_->GetEntityByUri(uri);
1057 
1058         if (CORE_NS::EntityUtil::IsValid(entity)) {
1059             BASE_NS::string name;
1060             if (sceneHolder_->GetEntityName(entity, name)) {
1061                 node = CreateNode(classId);
1062                 if (node) {
1063                     auto ecsScene = GetSelf<SCENE_NS::IEcsScene>();
1064                     auto ecsObject = CreateNewEcsObject({});
1065                     auto nodeInterface = interface_pointer_cast<INodeEcsInterfacePrivate>(node);
1066                     nodeInterface->Initialize(ecsScene, ecsObject, {}, "", name, sceneHolder_, entity);
1067                 }
1068             }
1069         }
1070 
1071         return node;
1072     }
1073 
BindNodeToEcs(SCENE_NS::INode::Ptr & node,const BASE_NS::string_view fullPath,bool createEngineObject)1074     void BindNodeToEcs(
1075         SCENE_NS::INode::Ptr& node, const BASE_NS::string_view fullPath, bool createEngineObject) override
1076     {
1077         SCENE_PLUGIN_VERBOSE_LOG("Scene::BindNodeToEcs called for %s", BASE_NS::string(fullPath).c_str());
1078 
1079         CORE_NS::Entity entity;
1080         auto ecsScene = GetSelf<SCENE_NS::IEcsScene>();
1081 
1082         bool addToRootContainer { false };
1083         BASE_NS::string nodePath;
1084         BASE_NS::string nodeName;
1085 
1086         auto classUid = interface_cast<META_NS::IObject>(node)->GetClassId();
1087         bool isResourceClassType = (classUid == SCENE_NS::ClassId::Material) || // Material
1088                                    (classUid == SCENE_NS::ClassId::Mesh) ||     // Mesh
1089                                    (classUid == SCENE_NS::ClassId::Animation);  // Animation
1090 
1091         auto isRealUri = fullPath.find("://") != BASE_NS::string_view::npos;
1092 
1093         if (!isRealUri) {
1094             auto cutIx = fullPath.find_last_of('/');
1095             if (cutIx != BASE_NS::string_view::npos && cutIx < fullPath.size()) {
1096                 ++cutIx;
1097                 nodePath = BASE_NS::string(fullPath.data(), cutIx);
1098                 nodeName = BASE_NS::string(fullPath.data() + cutIx, fullPath.size() - cutIx);
1099             } else if (!fullPath.empty()) {
1100                 nodeName = BASE_NS::string(fullPath.data(), fullPath.size());
1101             }
1102         }
1103 
1104         if (nodeName.empty()) {
1105             if (createEngineObject) {
1106                 nodeName = interface_cast<META_NS::IObjectInstance>(node)->GetInstanceId().ToString();
1107             } else {
1108                 if (isRealUri) {
1109                     auto ecsObject = CreateNewEcsObject({});
1110                     auto nodeInterface = interface_cast<INodeEcsInterfacePrivate>(node);
1111                     nodeInterface->Initialize(ecsScene, ecsObject, {}, "", BASE_NS::string(fullPath), sceneHolder_, {});
1112                 } else {
1113                     CORE_LOG_W("%s: refusing to create proxy object without valid target, name is empty.", __func__);
1114                 }
1115                 return;
1116             }
1117         }
1118 
1119         auto nodeInterface = interface_cast<INodeEcsInterfacePrivate>(node);
1120 
1121         if (nodeInterface->EcsObject() && CORE_NS::EntityUtil::IsValid(nodeInterface->EcsObject()->GetEntity())) {
1122             CORE_LOG_W("%s: refusing to recreate proxy object while current one is valid: %s", __func__,
1123                 BASE_NS::string(fullPath).c_str());
1124             return;
1125         }
1126 
1127         auto ecsObject = CreateNewEcsObject({});
1128 
1129             // NEW NODE
1130         if (createEngineObject) {
1131             auto classUid = interface_pointer_cast<META_NS::IObject>(node)->GetClassId();
1132             auto constructNode = [this, nodePath, nodeName, classUid](
1133                                          const auto& sceneHolder) -> CORE_NS::Entity {
1134                     CORE_NS::Entity entity;
1135                 if (sceneHolder) {
1136                     // Should not need to type this deep here
1137                     if (classUid == SCENE_NS::ClassId::Material) {
1138                         entity = sceneHolder->CreateMaterial(nodeName);
1139                     } else if (classUid == SCENE_NS::ClassId::Camera) {
1140                         entity = sceneHolder->CreateCamera(nodePath, nodeName, 0);
1141                     } else if (auto node = sceneHolder->CreateNode(nodePath, nodeName)) {
1142                         entity = node->GetEntity();
1143 
1144                         sceneHolder->EnableLayerComponent(entity);
1145                         if (classUid == SCENE_NS::ClassId::Light) {
1146                             sceneHolder->EnableLightComponent(entity);
1147                         }
1148                         if (classUid == SCENE_NS::ClassId::Environment) {
1149                             sceneHolder->EnableEnvironmentComponent(entity);
1150                         }
1151                     }
1152                 }
1153 
1154                 return entity;
1155             };
1156             if (GetValue(Asynchronous())) {
1157                 AddEngineTask(MakeTask(
1158                                   [constructNode](const auto& sceneHolder) {
1159                                       constructNode(sceneHolder);
1160                                       return false;
1161                                   },
1162                                   sceneHolder_),
1163                     false);
1164             } else {
1165                 entity = constructNode(sceneHolder_);
1166             }
1167 
1168             addToRootContainer = true;
1169             // If we don't have parent .. then attach to root.
1170             if (nodePath.empty() && !isResourceClassType) {
1171                 nodePath = "/" + rootNodeId_ + "/";
1172             }
1173 
1174             if (META_NS::Property<uint32_t> creationPolicy = nodeInterface->GetLifecycleInfo()) {
1175                 creationPolicy->SetValue(NODE_LC_CREATED);
1176                 nodeInterface->ClaimOwnershipOfEntity(true);
1177             }
1178         } else {
1179             // We expect to find the node from ecs (sooner or later)
1180             // If we are running synchronously, we could check it and even
1181             // tell the calling code if the node is there.
1182 
1183             // We'd need to know some additional info about the node parent etc
1184             if (META_NS::Property<uint32_t> creationPolicy = nodeInterface->GetLifecycleInfo()) {
1185                 creationPolicy->SetValue(NODE_LC_MIRROR_EXISTING);
1186             }
1187         }
1188 
1189         SCENE_NS::INode::Ptr parent;
1190 
1191         if (!isResourceClassType) {
1192             auto containable = interface_cast<META_NS::IContainable>(node);
1193             if (containable->GetParent()) {
1194                 parent = interface_pointer_cast<SCENE_NS::INode>(containable->GetParent());
1195             }
1196 
1197             if (!parent) {
1198                 parent = addToRootContainer ? RootNode()->GetValue() : currentParent_;
1199             }
1200         }
1201 
1202         nodeInterface->Initialize(ecsScene, ecsObject, parent, nodePath, nodeName, sceneHolder_, entity);
1203     }
1204 
UpdateCachedNodePath(const SCENE_NS::INode::Ptr & node)1205     void UpdateCachedNodePath(const SCENE_NS::INode::Ptr& node) override
1206     {
1207         if (node) {
1208             if (interface_cast<META_NS::IObject>(node)->GetClassId() != SCENE_NS::ClassId::Node) {
1209                 UpdateCachedReference(node);
1210                 return;
1211             }
1212 
1213             bool found = false;
1214 
1215             for (auto&& ite : nodes_) {
1216                 if (ite.second == node) {
1217                     nodes_.erase(ite.first);
1218                     break;
1219                 }
1220             }
1221 
1222             BASE_NS::string pathString = node->Path()->GetValue();
1223             pathString.append(node->Name()->GetValue());
1224             nodes_[pathString] = node;
1225         }
1226     }
1227 
SetCacheEnabled(const SCENE_NS::INode::Ptr & node,bool enabled)1228     void SetCacheEnabled(const SCENE_NS::INode::Ptr& node, bool enabled) override
1229     {
1230         if (!node) {
1231             return;
1232         }
1233 
1234         if (enabled) {
1235             BASE_NS::string pathString = node->Path()->GetValue();
1236             pathString.append(node->Name()->GetValue());
1237             nodes_[pathString] = node;
1238         } else {
1239             for (auto&& ite : nodes_) {
1240                 if (ite.second == node) {
1241                     nodes_.erase(ite.first);
1242                     break;
1243                 }
1244             }
1245         }
1246     }
1247 
1248     typedef BASE_NS::shared_ptr<CORE_NS::IInterface> (*fun)(SceneImpl* me, const BASE_NS::string_view);
1249 
relmat(SceneImpl * me,const BASE_NS::string_view p)1250     static BASE_NS::shared_ptr<CORE_NS::IInterface> relmat(SceneImpl* me, const BASE_NS::string_view p)
1251     {
1252         return me->ReleaseMaterial(p);
1253     }
relmesh(SceneImpl * me,const BASE_NS::string_view p)1254     static BASE_NS::shared_ptr<CORE_NS::IInterface> relmesh(SceneImpl* me, const BASE_NS::string_view p)
1255     {
1256         return me->ReleaseMesh(p);
1257     }
relanim(SceneImpl * me,const BASE_NS::string_view p)1258     static BASE_NS::shared_ptr<CORE_NS::IInterface> relanim(SceneImpl* me, const BASE_NS::string_view p)
1259     {
1260         return me->ReleaseAnimation(p);
1261     }
relnode(SceneImpl * me,const BASE_NS::string_view p)1262     static BASE_NS::shared_ptr<CORE_NS::IInterface> relnode(SceneImpl* me, const BASE_NS::string_view p)
1263     {
1264         return me->ReleaseNode(p);
1265     }
1266     template<typename Type>
CacheNode(const char * const tname,const SCENE_NS::INode::Ptr & node,BASE_NS::unordered_map<BASE_NS::string,typename Type::WeakPtr> & cache,fun func)1267     bool CacheNode(const char* const tname, const SCENE_NS::INode::Ptr& node,
1268         BASE_NS::unordered_map<BASE_NS::string, typename Type::WeakPtr>& cache, fun func)
1269     {
1270         if (auto typed = interface_pointer_cast<Type>(node)) {
1271             BASE_NS::string uri;
1272             bool found = false;
1273 
1274             if (node->GetInterface(SCENE_NS::ICamera::UID)) {
1275                 // handle camera naming..
1276                 uri = node->Path()->GetValue();
1277                 uri.append(node->Name()->GetValue());
1278 
1279             } else {
1280                 auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(typed);
1281                 uri = sceneHolder_->GetResourceId(ecsObject->GetEntity());
1282                 if (uri.empty()) {
1283                     uri = node->Name()->GetValue() + ":" + BASE_NS::to_hex(ecsObject->GetEntity().id);
1284                 }
1285             }
1286 
1287             for (auto&& ite : cache) {
1288                 if (ite.second.lock() == typed) {
1289                     if (uri != ite.first) {
1290                         cache[uri] = interface_pointer_cast<Type>(func(this, ite.first));
1291                         SCENE_PLUGIN_VERBOSE_LOG("Updating cached reference of %s: %s", tname, uri.c_str());
1292                     }
1293                     found = true;
1294                     break; // reference is valid so return true regardless if the node was moved
1295                 }
1296             }
1297 
1298             if (!found) {
1299                 SCENE_PLUGIN_VERBOSE_LOG("Caching reference of %s: %s", tname, uri.c_str());
1300                 cache[uri] = typed;
1301             }
1302             return true;
1303         }
1304         return false;
1305     }
UpdateCachedReference(const SCENE_NS::INode::Ptr & node)1306     void UpdateCachedReference(const SCENE_NS::INode::Ptr& node) override
1307     {
1308         if (node) {
1309             if (CacheNode<SCENE_NS::IMaterial>("material", node, materials_, relmat) ||
1310                 CacheNode<SCENE_NS::IMesh>("mesh", node, meshes_, relmesh) ||
1311                 CacheNode<META_NS::IAnimation>("animation", node, animations_, relanim)) {
1312                 // completed. (meshes, materials and animations are not added to node list)
1313                 return;
1314             }
1315             if (CacheNode<SCENE_NS::ICamera>("camera", node, cameras_, relnode)) {
1316                 // camera cache updated.
1317                 auto* obj = interface_cast<SCENE_NS::IEcsObject>(node);
1318                 if (obj) {
1319                     auto entity = obj->GetEntity();
1320                     if (CORE_NS::EntityUtil::IsValid(entity)) {
1321                         sceneHolder_->AddCamera(entity);
1322                     } else {
1323                         CORE_LOG_V("camera has no entity id yet");
1324                     }
1325                 } else {
1326                     CORE_LOG_V("camera has no IEcsObject");
1327                 }
1328             }
1329             // update node cache.
1330 
1331             bool found = false;
1332             BASE_NS::string uri = node->Path()->GetValue();
1333             uri.append(node->Name()->GetValue());
1334 
1335             for (auto&& ite : nodes_) {
1336                 if (ite.second == node) {
1337                     if (uri != ite.first) {
1338                         nodes_[uri] = ReleaseNode(ite.first);
1339                     }
1340                     found = true;
1341                     break; // reference is valid so return true regardless if the node was moved
1342                 }
1343             }
1344 
1345             if (!found) {
1346                 nodes_[uri] = node;
1347             }
1348         }
1349     }
1350 
1351     // Release Node Reference from cache
ReleaseNode(const BASE_NS::string_view name)1352     SCENE_NS::INode::Ptr ReleaseNode(const BASE_NS::string_view name) override
1353     {
1354         SCENE_NS::INode::Ptr ret {};
1355         if (auto ite = nodes_.extract(BASE_NS::string(name)); !ite.empty()) {
1356             ret = BASE_NS::move(ite.mapped());
1357             if (auto privateIntf = interface_cast<INodeEcsInterfacePrivate>(ret)) {
1358                 privateIntf->ClaimOwnershipOfEntity(true);
1359             }
1360             RemoveNodeFromCurrentContainer(ret);
1361             if (auto cam = interface_pointer_cast<SCENE_NS::ICamera>(ret)) {
1362                 ReleaseCamera(name);
1363             }
1364         }
1365         return ret;
1366     }
ReleaseNode(const SCENE_NS::INode::Ptr & node)1367     void ReleaseNode(const SCENE_NS::INode::Ptr& node) override
1368     {
1369         if (node) {
1370             ReleaseNode(node->Path()->GetValue() + node->Name()->GetValue());
1371         }
1372     }
1373 
ReleaseMaterial(const BASE_NS::string_view name)1374     SCENE_NS::IMaterial::Ptr ReleaseMaterial(const BASE_NS::string_view name) override
1375     {
1376         SCENE_NS::IMaterial::Ptr ret {};
1377         if (auto ite = materials_.find(name); ite != materials_.end()) {
1378             ret = ite->second.lock();
1379             materials_.erase(ite);
1380         }
1381         return ret;
1382     }
1383 
ReleaseMesh(const BASE_NS::string_view name)1384     SCENE_NS::IMesh::Ptr ReleaseMesh(const BASE_NS::string_view name) override
1385     {
1386         SCENE_NS::IMesh::Ptr ret {};
1387         if (auto ite = meshes_.find(name); ite != meshes_.end()) {
1388             ret = ite->second.lock();
1389             meshes_.erase(ite);
1390         }
1391 
1392         return ret;
1393     }
1394 
ReleaseAnimation(const BASE_NS::string_view name)1395     META_NS::IAnimation::Ptr ReleaseAnimation(const BASE_NS::string_view name) override
1396     {
1397         META_NS::IAnimation::Ptr ret {};
1398         if (auto ite = animations_.find(name); ite != animations_.end()) {
1399             ret = ite->second.lock();
1400             animations_.erase(ite);
1401         }
1402         return ret;
1403     }
1404 
ReleaseCamera(const BASE_NS::string_view name)1405     SCENE_NS::ICamera::Ptr ReleaseCamera(const BASE_NS::string_view name)
1406     {
1407         SCENE_NS::ICamera::Ptr ret {};
1408         if (auto ite = cameras_.find(name); ite != cameras_.end()) {
1409             ret = ite->second.lock();
1410             // make sure the rendertarget/bitmap is also released.
1411             SetBitmap(nullptr, ret);
1412             cameras_.erase(ite);
1413         }
1414         return ret;
1415     }
1416 
CreateMeshFromArraysI16(const BASE_NS::string_view name,SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays)1417     SCENE_NS::IMesh::Ptr CreateMeshFromArraysI16(
1418         const BASE_NS::string_view name, SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays) override
1419     {
1420         return CreateMeshFromArrays<uint16_t>(name, arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT16);
1421     }
1422 
CreateMeshFromArraysI32(const BASE_NS::string_view name,SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays)1423     SCENE_NS::IMesh::Ptr CreateMeshFromArraysI32(
1424         const BASE_NS::string_view name, SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays) override
1425     {
1426         return CreateMeshFromArrays<uint32_t>(name, arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT32);
1427     }
1428 
1429     template<typename IndicesType>
CreateMeshFromArrays(const BASE_NS::string_view name,SCENE_NS::MeshGeometryArrayPtr<IndicesType> arrays,RENDER_NS::IndexType indexType)1430     SCENE_NS::IMesh::Ptr CreateMeshFromArrays(const BASE_NS::string_view name,
1431         SCENE_NS::MeshGeometryArrayPtr<IndicesType> arrays, RENDER_NS::IndexType indexType)
1432     {
1433         auto nameString = BASE_NS::shared_ptr(new BASE_NS::string(name.data(), name.size()));
1434 
1435         AddEngineTask(MakeTask(
1436                           [arrays, nameString, indexType](auto sceneHolder) {
1437                               auto meshEntity = sceneHolder->template CreateMeshFromArrays<IndicesType>(
1438                                   *nameString, arrays, indexType);
1439                               if (!sceneHolder->IsAsync()) {
1440                                   *nameString = sceneHolder->GetResourceId(meshEntity);
1441                               }
1442 
1443                               return false;
1444                           },
1445                           sceneHolder_),
1446             false);
1447         return GetMesh(*nameString);
1448     }
1449 
InstantiateMaterialProxies()1450     void InstantiateMaterialProxies() override
1451     {
1452         AddEngineTask(MakeTask(
1453                           [me = BASE_NS::weak_ptr(GetSelf<SCENE_NS::IScene>())](auto sceneHolder) {
1454                               auto ids = sceneHolder->ListMaterialNames();
1455                               sceneHolder->QueueApplicationTask(MakeTask(
1456                                                                     [ids](auto self) {
1457                                                                         for (auto& id : *ids) {
1458                                                                             self->GetMaterial(id);
1459                                                                         }
1460                                                                         return false;
1461                                                                     },
1462                                                                     me),
1463                                   false);
1464                               return false;
1465                           },
1466                           sceneHolder_),
1467             false);
1468     }
1469 
InstantiateMeshProxies()1470     void InstantiateMeshProxies() override
1471     {
1472         AddEngineTask(MakeTask(
1473                           [me = BASE_NS::weak_ptr(GetSelf<SCENE_NS::IScene>())](auto sceneHolder) {
1474                               auto ids = sceneHolder->ListMeshNames();
1475                               sceneHolder->QueueApplicationTask(MakeTask(
1476                                                                     [ids](auto self) {
1477                                                                         for (auto& id : *ids) {
1478                                                                             self->GetMesh(id);
1479                                                                         }
1480 
1481                                                                         return false;
1482                                                                     },
1483                                                                     me),
1484                                   false);
1485 
1486                               return false;
1487                           },
1488                           sceneHolder_),
1489             false);
1490     }
1491 
1492 public:
~SceneImpl()1493     ~SceneImpl() override
1494     {
1495         allAnims_.clear();
1496         if (sceneHolder_) {
1497             DetachScene();
1498         }
1499 
1500         cameraNodePtr_.reset();
1501         rootNodePtr_.reset();
1502 
1503         UnsubscribeFromPropertyChanges();
1504 
1505         if (sceneHolder_) {
1506             sceneHolder_->Uninitialize();
1507             sceneHolder_.reset();
1508         }
1509     }
1510 
SetEcsInitializationCallback(IPrepareSceneForInitialization::WeakPtr callback)1511     void SetEcsInitializationCallback(IPrepareSceneForInitialization::WeakPtr callback) override
1512     {
1513         if (sceneHolder_) {
1514             sceneHolder_->SetEcsInitializationCallback(callback);
1515         } else {
1516             CORE_LOG_W("%s: sceneholder does not exist, cannot set callback", __func__);
1517         }
1518     }
1519 
GetWorldAABB(const BASE_NS::Math::Mat4X4 & world,const BASE_NS::Math::Vec3 & aabbMin,const BASE_NS::Math::Vec3 & aabbMax)1520     SCENE_NS::IPickingResult::Ptr GetWorldAABB(const BASE_NS::Math::Mat4X4& world, const BASE_NS::Math::Vec3& aabbMin,
1521         const BASE_NS::Math::Vec3& aabbMax) override
1522     {
1523         auto ret = META_NS::GetObjectRegistry().Create<SCENE_NS::IPickingResult>(SCENE_NS::ClassId::PendingVec3Request);
1524         if (ret) {
1525             AddEngineTask(
1526                 META_NS::MakeCallback<META_NS::ITaskQueueTask>([w = world, min = aabbMin, max = aabbMax,
1527                                                                    weakRet = BASE_NS::weak_ptr(ret),
1528                                                                    weakSh = BASE_NS::weak_ptr(sceneHolder_)]() {
1529                     if (auto sh = weakSh.lock()) {
1530                         if (auto ret = weakRet.lock()) {
1531                             if (sh->GetWorldAABB(ret, w, min, max)) {
1532                                 sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet]() {
1533                                     if (auto writable =
1534                                             interface_pointer_cast<SCENE_NS::IPendingRequestData<BASE_NS::Math::Vec3>>(
1535                                                 weakRet)) {
1536                                         writable->MarkReady();
1537                                     }
1538                                     return false;
1539                                 }),
1540                                     false);
1541                             }
1542                         }
1543                     }
1544                     return false;
1545                 }),
1546                 false);
1547             return ret;
1548         }
1549         return SCENE_NS::IPickingResult::Ptr();
1550     }
1551 
RayCast(const BASE_NS::Math::Vec3 & start,const BASE_NS::Math::Vec3 & direction)1552     SCENE_NS::IRayCastResult::Ptr RayCast(
1553         const BASE_NS::Math::Vec3& start, const BASE_NS::Math::Vec3& direction) override
1554     {
1555         auto ret =
1556             META_NS::GetObjectRegistry().Create<SCENE_NS::IRayCastResult>(SCENE_NS::ClassId::PendingDistanceRequest);
1557         if (ret) {
1558             AddEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([origin = start, dir = direction,
1559                                                                              weakRet = BASE_NS::weak_ptr(ret),
1560                                                                              weakSh = BASE_NS::weak_ptr(sceneHolder_),
1561                                                                              weakSelf = BASE_NS::weak_ptr(
1562                                                                                  GetSelf<SCENE_NS::IScene>())]() {
1563                 if (auto sh = weakSh.lock()) {
1564                     if (auto ret = weakRet.lock()) {
1565                         if (sh->RayCast(ret, origin, dir)) {
1566                             sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet,
1567                                                                                                         weakSelf]() {
1568                                 if (auto writable =
1569                                         interface_pointer_cast<SCENE_NS::IPendingRequestData<SCENE_NS::NodeDistance>>(
1570                                             weakRet)) {
1571                                     if (auto self = weakSelf.lock()) {
1572                                         // resolve proxy nodes
1573                                         for (size_t ii = writable->MetaData().size(); ii > 0;) {
1574                                             --ii;
1575                                             writable->MutableData().at(ii).node =
1576                                                 self->GetNode(writable->MetaData().at(ii));
1577                                         }
1578                                         writable->MarkReady();
1579                                     }
1580                                 }
1581                                 return false;
1582                             }),
1583                                 false);
1584                         }
1585                     }
1586                 }
1587                 return false;
1588             }),
1589                 false);
1590             return ret;
1591         }
1592         return SCENE_NS::IRayCastResult::Ptr();
1593     }
1594 
RayCast(const BASE_NS::Math::Vec3 & start,const BASE_NS::Math::Vec3 & direction,uint64_t layerMask)1595     SCENE_NS::IRayCastResult::Ptr RayCast(
1596         const BASE_NS::Math::Vec3& start, const BASE_NS::Math::Vec3& direction, uint64_t layerMask) override
1597     {
1598         auto ret =
1599             META_NS::GetObjectRegistry().Create<SCENE_NS::IRayCastResult>(SCENE_NS::ClassId::PendingDistanceRequest);
1600         if (ret) {
1601             AddEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([origin = start, dir = direction, layerMask,
1602                                                                              weakRet = BASE_NS::weak_ptr(ret),
1603                                                                              weakSh = BASE_NS::weak_ptr(sceneHolder_),
1604                                                                              weakSelf = BASE_NS::weak_ptr(
1605                                                                                  GetSelf<SCENE_NS::IScene>())]() {
1606                 if (auto sh = weakSh.lock()) {
1607                     if (auto ret = weakRet.lock()) {
1608                         if (sh->RayCast(ret, origin, dir, layerMask)) {
1609                             sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet,
1610                                                                                                         weakSelf]() {
1611                                 if (auto writable =
1612                                         interface_pointer_cast<SCENE_NS::IPendingRequestData<SCENE_NS::NodeDistance>>(
1613                                             weakRet)) {
1614                                     if (auto self = weakSelf.lock()) {
1615                                         // resolve proxy nodes
1616                                         for (size_t ii = writable->MetaData().size(); ii > 0;) {
1617                                             --ii;
1618                                             writable->MutableData().at(ii).node =
1619                                                 self->GetNode(writable->MetaData().at(ii));
1620                                         }
1621                                         writable->MarkReady();
1622                                     }
1623                                 }
1624                                 return false;
1625                             }),
1626                                 false);
1627                         }
1628                     }
1629                 }
1630                 return false;
1631             }),
1632                 false);
1633             return ret;
1634         }
1635         return SCENE_NS::IRayCastResult::Ptr();
1636     }
RenderCameras()1637     BASE_NS::vector<CORE_NS::Entity> RenderCameras() override
1638     {
1639         if (sceneHolder_) {
1640             return sceneHolder_->RenderCameras();
1641         }
1642         return {};
1643     }
ActivateCamera(const SCENE_NS::ICamera::Ptr & camera)1644     void ActivateCamera(const SCENE_NS::ICamera::Ptr& camera) override
1645     {
1646         if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
1647             auto ent = e->GetEntity();
1648             sceneHolder_->ActivateCamera(ent);
1649         }
1650     }
1651 
DeactivateCamera(const SCENE_NS::ICamera::Ptr & camera)1652     void DeactivateCamera(const SCENE_NS::ICamera::Ptr& camera) override
1653     {
1654         if (!camera) {
1655             return;
1656         }
1657 
1658         if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
1659             sceneHolder_->DeactivateCamera(e->GetEntity());
1660         }
1661     }
IsCameraActive(const SCENE_NS::ICamera::Ptr & camera)1662     bool IsCameraActive(const SCENE_NS::ICamera::Ptr& camera) override
1663     {
1664         if (!camera) {
1665             return false;
1666         }
1667 
1668         if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
1669             return sceneHolder_->IsCameraActive(e->GetEntity());
1670         }
1671         return false;
1672     }
1673 
1674 private:
SubscribeToPropertyChanges()1675     void SubscribeToPropertyChanges()
1676     {
1677         if (RootNode()) {
1678             rootNodeChangedToken_ = RootNode()->OnChanged()->AddHandler(
1679                 META_NS::MakeCallback<META_NS::IOnChanged>(this, &SceneImpl::OnRootNodeChanged));
1680         }
1681 
1682         if (Uri()) {
1683             uriHandlerToken_ = Uri()->OnChanged()->AddHandler(
1684                 META_NS::MakeCallback<META_NS::IOnChanged>([this]() { this->Load(Uri()->GetValue()); }));
1685         }
1686 
1687         // Start listening changes of the scene controller properties. These may go to different place some day
1688         if (SystemGraphUri()) {
1689             systemGraphUriHandlerToken_ = SystemGraphUri()->OnChanged()->AddHandler(
1690                 META_NS::MakeCallback<META_NS::IOnChanged>(this, &SceneImpl::onSystemGraphUriChanged));
1691         }
1692     }
1693 
UnsubscribeFromPropertyChanges()1694     void UnsubscribeFromPropertyChanges()
1695     {
1696         if (Uri()) {
1697             Uri()->OnChanged()->RemoveHandler(uriHandlerToken_);
1698             uriHandlerToken_ = {};
1699         }
1700 
1701         if (RootNode()) {
1702             RootNode()->OnChanged()->RemoveHandler(rootNodeChangedToken_);
1703             rootNodeChangedToken_ = {};
1704         }
1705 
1706         if (SystemGraphUri()) {
1707             SystemGraphUri()->OnChanged()->RemoveHandler(systemGraphUriHandlerToken_);
1708             systemGraphUriHandlerToken_ = {};
1709         }
1710 
1711         if (Asynchronous()) {
1712             Asynchronous()->OnChanged()->RemoveHandler(asyncChangedToken_);
1713             asyncChangedToken_ = {};
1714         }
1715     }
1716 
OnRootNodeChanged()1717     void OnRootNodeChanged()
1718     {
1719         auto contentObject = interface_pointer_cast<META_NS::IObject>(RootNode()->GetValue());
1720         contentImpl_->SetContent(contentObject);
1721 
1722         META_NS::HierarchyChangeModeValue changeMode;
1723         changeMode.Set(META_NS::HierarchyChangeMode::NOTIFY_CONTAINER);
1724         hierarchyController_->SetTarget(contentObject, changeMode);
1725     }
1726 
1727     META_NS::IEvent::Token systemGraphUriHandlerToken_ {};
1728     META_NS::IEvent::Token renderSizeHandlerToken_ {};
1729     META_NS::IEvent::Token cameraHandlerToken_ {};
1730     META_NS::IEvent::Token uriHandlerToken_ {};
1731     META_NS::IEvent::Token rootNodeChangedToken_ {};
1732     META_NS::IEvent::Token renderModeChangedToken_ {};
1733     META_NS::IEvent::Token asyncChangedToken_ {};
1734 
1735     SceneHolder::Ptr sceneHolder_;
1736 
1737     BASE_NS::string rootNodeId_;
1738     BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::INode::Ptr> nodes_;
1739     BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::ICamera::WeakPtr> cameras_;
1740     BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMesh::WeakPtr> meshes_;
1741     BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMaterial::WeakPtr> materials_;
1742     BASE_NS::unordered_map<BASE_NS::string, META_NS::IAnimation::WeakPtr> animations_;
1743 
1744     BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMaterial::WeakPtr> uriMaterials_;
1745 
1746     uint64_t instanceNumber_ { 0 };
1747 
1748     // Preserve property instances while scene / ecs is invalid
1749     SCENE_NS::INode::Ptr rootNodePtr_ {};
1750     SCENE_NS::ICamera::Ptr cameraNodePtr_ {};
1751     META_NS::IContent::Ptr contentImpl_ {};
1752 
1753     uint64_t defaultCameraHandle_ { 0 };
1754 
1755     // We need to add a node onto a container in bit awkward position (without exposing it to a public api)
1756     // Store it here.
1757     SCENE_NS::INode::Ptr currentParent_ {};
1758     META_NS::IObjectHierarchyObserver::Ptr hierarchyController_;
1759 
1760     META_NS::IAnimationController::Ptr animationController_;
1761 };
1762 
GetAnimations() const1763 BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> SceneImpl::GetAnimations() const
1764 {
1765     return animationController_->GetAnimations();
1766 }
GetRunning() const1767 BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> SceneImpl::GetRunning() const
1768 {
1769     return animationController_->GetRunning();
1770 }
AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation> & animation)1771 bool SceneImpl::AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation)
1772 {
1773     return animationController_->AddAnimation(animation);
1774 }
RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation> & animation)1775 bool SceneImpl::RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation)
1776 {
1777     return animationController_->RemoveAnimation(animation);
1778 }
Clear()1779 void SceneImpl::Clear()
1780 {
1781     animationController_->Clear();
1782 }
Step(const META_NS::IClock::ConstPtr & clock)1783 META_NS::IAnimationController::StepInfo SceneImpl::Step(const META_NS::IClock::ConstPtr& clock)
1784 {
1785     return animationController_->Step(clock);
1786 }
1787 
1788 } // namespace
SCENE_BEGIN_NAMESPACE()1789 SCENE_BEGIN_NAMESPACE()
1790 void RegisterSceneImpl()
1791 {
1792     META_NS::GetObjectRegistry().RegisterObjectType<SceneImpl>();
1793 }
UnregisterSceneImpl()1794 void UnregisterSceneImpl()
1795 {
1796     META_NS::GetObjectRegistry().UnregisterObjectType<SceneImpl>();
1797 }
1798 SCENE_END_NAMESPACE()
1799