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 "node_impl.h"
16
17#include "task_utils.h"
18
19using SCENE_NS::MakeTask;
20
21#include <scene_plugin/api/material_uid.h>
22#include <scene_plugin/api/mesh_uid.h>
23#include <scene_plugin/api/scene_uid.h>
24
25#include "intf_multi_mesh_initialization.h"
26
27bool HasChangedProperties(META_NS::IMetadata& meta)
28{
29    for (auto& property : meta.GetAllProperties()) {
30        bool isInternal = META_NS::IsFlagSet(property, META_NS::ObjectFlagBits::INTERNAL);
31        if (isInternal) {
32            continue;
33        }
34
35        bool isSerializable = META_NS::IsFlagSet(property, META_NS::ObjectFlagBits::SERIALIZE);
36        if (isSerializable && property->IsValueSet()) {
37            return true;
38        }
39    }
40
41    return false;
42}
43
44namespace {
45
46size_t GetChildIndex(SCENE_NS::INode& parent, SCENE_NS::INode& child)
47{
48    auto object = interface_cast<META_NS::IObject>(&child);
49
50    auto container = interface_cast<META_NS::IContainer>(&parent);
51    if (container) {
52        for (auto i = 0; i < container->GetSize(); ++i) {
53            if (container->GetAt(i).get() == object) {
54                return i;
55            }
56        }
57    }
58
59    return SIZE_MAX;
60}
61
62} // namespace
63
64#include "bind_templates.inl"
65
66// implements CORE_NS::IInterface
67const CORE_NS::IInterface* NodeImpl::GetInterface(const BASE_NS::Uid& uid) const
68{
69    if (META_NS::TypeId(uid) == SCENE_NS::InterfaceId::IEnvironment.Id()) {
70        return environment_.get();
71    } else {
72        return Fwd::GetInterface(uid);
73    }
74}
75
76CORE_NS::IInterface* NodeImpl::GetInterface(const BASE_NS::Uid& uid)
77{
78    if (META_NS::TypeId(uid) == SCENE_NS::InterfaceId::IEnvironment.Id()) {
79        return environment_.get();
80    } else {
81        return Fwd::GetInterface(uid);
82    }
83}
84
85SCENE_NS::NodeState NodeImpl::GetAttachedState() const
86{
87    return attachedState_;
88}
89
90bool NodeImpl::IsConnected()
91{
92    if (Status()->GetValue() == SCENE_NS::INode::NODE_STATUS_CONNECTED ||
93        Status()->GetValue() == SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED)
94        return true;
95
96    return false;
97}
98
99void NodeImpl::DisableInputHandling()
100{
101    /*auto inputMode = META_ACCESS_PROPERTY(InputMode);
102    if (auto i = interface_cast<META_NS::IMetaPropertyInternal>(inputMode)) {
103        auto flags = inputMode->Flags();
104        flags &= ~META_NS::IMetaProperty::PropertyFlagsValue(META_NS::IMetaProperty::PropertyFlagBits::SERIALIZABLE);
105        flags |= META_NS::IMetaProperty::PropertyFlagsValue(META_NS::IMetaProperty::PropertyFlagBits::INTERNAL);
106        i->SetFlags(flags);
107    }*/
108}
109
110bool NodeImpl::Connect(const INode::Ptr& parent)
111{
112    SCENE_PLUGIN_VERBOSE_LOG("Node::Connect called for: '%s' (%s)", GetName().c_str(), Path()->Get().c_str());
113    if (parent) {
114        auto sceneObject = interface_pointer_cast<IObject>(parent->GetScene());
115        if (sceneObject) {
116            if (auto sceneInterface = interface_pointer_cast<SCENE_NS::IEcsScene>(sceneObject)) {
117                CORE_ASSERT(sceneInterface);
118
119                BASE_NS::string path = "/";
120                if (parent) {
121                    path = parent->Path()->GetValue() + parent->Name()->GetValue() + "/";
122                }
123
124                META_ACCESS_PROPERTY(Path)->SetValue(path);
125
126                if (!ecsObject_) {
127                    EnsureEcsBinding(sceneInterface);
128                }
129
130                return true;
131            }
132        }
133    }
134
135    SCENE_PLUGIN_VERBOSE_LOG("Node::Connect - NO PARENT - '%s'", GetName().c_str());
136    return false;
137}
138
139void NodeImpl::Activate()
140{
141    auto parent = interface_pointer_cast<INode>(GetParent());
142
143    if (auto sceneHolder = SceneHolder()) {
144        size_t index = SIZE_MAX;
145        BASE_NS::string path = "/";
146        if (parent) {
147            path = parent->Path()->GetValue() + parent->Name()->GetValue() + "/";
148            index = GetChildIndex(*parent, *this);
149        }
150        sceneHolder->QueueEngineTask(
151            META_NS::MakeCallback<META_NS::ITaskQueueTask>(
152                [path, index, e = ecsObject_->GetEntity(), weak_sh = BASE_NS::weak_ptr(sceneHolder)] {
153                    auto sh = weak_sh.lock();
154                    if (sh) {
155                        sh->SetEntityActive(e, true);
156                        sh->ReparentEntity(e, path, index);
157                    }
158                    return false;
159                }),
160            false);
161        META_ACCESS_PROPERTY(Path)->SetValue(path);
162        UpdateChildrenPath();
163        RecursivelyEnableCache(true);
164    }
165}
166
167void NodeImpl::Deactivate()
168{
169    if (auto sceneHolder = SceneHolder()) {
170        // TODO: Check if we can set parent to "invalid entity".
171        sceneHolder->QueueEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(
172                                         [e = ecsObject_->GetEntity(), weak_sh = BASE_NS::weak_ptr(sceneHolder)] {
173                                             auto sh = weak_sh.lock();
174                                             if (sh) {
175                                                 sh->SetEntityActive(e, false);
176                                             }
177                                             return false;
178                                         }),
179            false);
180
181        RecursivelyEnableCache(false);
182    }
183}
184
185void NodeImpl::AttachToHierarchy()
186{
187    SCENE_PLUGIN_VERBOSE_LOG(
188        "Node::AttachToHierarchy called for: '%s' (%s)", GetName().c_str(), Path()->GetValue().c_str());
189
190    // If we have an entity, that we simply need to activate.
191    // If we are unbound, we need to try to bind this node to scene graph.
192    if (IsConnected()) {
193        Activate();
194    } else {
195        Connect(interface_pointer_cast<INode>(GetParent()));
196    }
197
198    attachedState_ = SCENE_NS::NodeState::ATTACHED;
199    SCENE_PLUGIN_VERBOSE_LOG("Node::AttachToHierarchy path is: '%s'", Path()->GetValue().c_str());
200}
201
202void NodeImpl::DetachFromHierarchy()
203{
204    SCENE_PLUGIN_VERBOSE_LOG(
205        "Node::DetachFromHierarchy called for: '%s' (%s)", GetName().c_str(), Path()->GetValue().c_str());
206
207    // We are being detachad from the scene graph.
208    if (IsConnected()) {
209        // We simply need to deactivate.
210        Deactivate();
211    }
212
213    // TODO: Is there something we need to do here?
214    attachedState_ = SCENE_NS::NodeState::DETACHED;
215
216    SCENE_PLUGIN_VERBOSE_LOG("Node::DetachFromHierarchy path is: '%s'", Path()->GetValue().c_str());
217}
218
219void NodeImpl::SubscribeToNameChanges()
220{
221    if (!nameChangedToken_) {
222        nameChangedToken_ = Name()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this] {
223            if (IsResourceClassType() || ownsEntity_) {
224                RenameEntity(META_ACCESS_PROPERTY(Name)->GetValue());
225            }
226        }));
227    }
228}
229
230void NodeImpl::UnsubscribeFromNameChanges()
231{
232    if (nameChangedToken_) {
233        Name()->OnChanged()->RemoveHandler(nameChangedToken_);
234        nameChangedToken_ = { 0 };
235    }
236}
237
238bool NodeImpl::IsResourceClassType()
239{
240    auto classUid = GetClassId();
241    bool isResourceClassType = (classUid == SCENE_NS::ClassId::Material) || // Material
242                               (classUid == SCENE_NS::ClassId::Mesh) ||     // Mesh
243                               (classUid == SCENE_NS::ClassId::Animation);  // Animation
244
245    return isResourceClassType;
246}
247/*
248bool NodeImpl::Import(
249    META_NS::Serialization::IImportContext& context, const META_NS::Serialization::ClassPrimitive& value)
250{
251    if (!Fwd::Import(context, value)) {
252        return false;
253    }
254
255    // Backwards compatibility.
256    auto prop = GetPropertyByName("SceneUid");
257    if (prop) {
258        RemoveProperty(prop);
259    }
260
261    prop = GetPropertyByName("Path");
262    if (auto path = META_NS::property_cast<BASE_NS::string>(prop)) {
263        path->Reset();
264    }
265
266    return true;
267}
268*/
269bool NodeImpl::Build(const IMetadata::Ptr& data)
270{
271    ClaimOwnershipOfEntity(false);
272
273    OnMoved()->AddHandler(META_NS::MakeCallback<META_NS::IOnChildMoved>([](const META_NS::ChildMovedInfo& info) {
274        auto parent = interface_cast<IObject>(info.parent.lock())->GetName();
275        auto object = info.object->GetName();
276        SCENE_PLUGIN_VERBOSE_LOG("Child '%s' moved in '%s'", object.c_str(), parent.c_str());
277    }));
278
279    PropertyNameMask().clear();
280    PropertyNameMask()[TRANSFORM_COMPONENT_NAME] = { TRANSFORM_POSITION.substr(TRANSFORM_COMPONENT_NAME_LEN),
281        TRANSFORM_SCALE.substr(TRANSFORM_COMPONENT_NAME_LEN), TRANSFORM_ROTATION.substr(TRANSFORM_COMPONENT_NAME_LEN) };
282    PropertyNameMask()[NODE_COMPONENT_NAME] = { NODE_ENABLED.substr(NODE_COMPONENT_NAME_LEN) };
283    PropertyNameMask()[LAYER_COMPONENT_NAME] = { LAYER_MASK.substr(LAYER_COMPONENT_NAME_LEN) };
284    PropertyNameMask()[LMATRIX_COMPONENT_NAME] = { LMATRIX_MATRIX.substr(LMATRIX_COMPONENT_NAME_LEN) };
285    PropertyNameMask()[RM_COMPONENT_NAME] = { RM_HANDLE.substr(RM_COMPONENT_NAME_LEN) };
286    PropertyNameMask()[ENVIRONMENT_COMPONENT_NAME] = {}; // read everything if it happens to be present
287
288    objectRegistry_ = &META_NS::GetObjectRegistry();
289    return true;
290}
291
292BASE_NS::string NodeImpl::GetName() const
293{
294    return META_NS::GetValue(Name());
295}
296
297CORE_NS::IEcs::Ptr NodeImpl::GetEcs() const
298{
299    if (ecsObject_) {
300        return ecsObject_->GetEcs();
301    }
302    return CORE_NS::IEcs::Ptr {};
303}
304
305void NodeImpl::SetEntity(CORE_NS::IEcs::Ptr ecs, CORE_NS::Entity entity)
306{
307    if (ecsObject_) {
308        return ecsObject_->SetEntity(ecs, entity);
309    }
310}
311
312CORE_NS::Entity NodeImpl::GetEntity() const
313{
314    if (ecsObject_) {
315        return ecsObject_->GetEntity();
316    }
317    return CORE_NS::Entity {};
318}
319
320void NodeImpl::BindObject(CORE_NS::IEcs::Ptr ecsInstance, CORE_NS::Entity entity)
321{
322    if (ecsObject_) {
323        ecsObject_->BindObject(ecsInstance, entity);
324    }
325}
326
327void NodeImpl::DefineTargetProperties(
328    BASE_NS::unordered_map<BASE_NS::string_view, BASE_NS::vector<BASE_NS::string_view>> names)
329{
330    if (ecsObject_) {
331        ecsObject_->DefineTargetProperties(names);
332    }
333}
334
335BASE_NS::vector<CORE_NS::Entity> NodeImpl::GetAttachments()
336{
337    if (ecsObject_) {
338        return ecsObject_->GetAttachments();
339    }
340    return BASE_NS::vector<CORE_NS::Entity> {};
341}
342
343void NodeImpl::AddAttachment(CORE_NS::Entity entity)
344{
345    if (ecsObject_) {
346        ecsObject_->AddAttachment(entity);
347    }
348}
349
350void NodeImpl::RemoveAttachment(CORE_NS::Entity entity)
351{
352    if (ecsObject_) {
353        ecsObject_->RemoveAttachment(entity);
354    }
355}
356
357void NodeImpl::CloneEcs(const BASE_NS::string& name, META_NS::IObject::Ptr target) const
358{
359    EcsScene()->AddEngineTask(
360        MakeTask(
361            [name, other = BASE_NS::weak_ptr(target)](auto self) {
362                if (auto sceneHolder = self->SceneHolder()) {
363                    // We have set the name unique, so for simplicity use it without padding
364                    auto clone = sceneHolder->CloneEntity(self->EcsObject()->GetEntity(), name, false);
365                    if (CORE_NS::EntityUtil::IsValid(clone)) {
366                        sceneHolder->QueueApplicationTask(MakeTask(
367                                                              [name](auto self, auto other) {
368                                                                  if (auto ret = static_pointer_cast<NodeImpl>(other)) {
369                                                                      META_NS::Property<uint32_t>(ret->GetLifecycleInfo())->SetValue(NODE_LC_CLONED);
370                                                                      ret->EnsureEcsBinding(self->EcsScene(), true);
371                                                                  }
372                                                                  return false;
373                                                              },
374                                                              self, other),
375                            false);
376                    }
377                }
378                return false;
379            },
380            static_pointer_cast<NodeImpl>(GetSelf())),
381        false);
382}
383
384/*META_NS::IObject::Ptr NodeImpl::GetClone(META_NS::ICloneableObject::CloneBehaviorFlags flags) const
385{
386    META_NS::IObject::Ptr ret = Super::GetClone(flags);
387    auto namebuf = BASE_NS::to_string(interface_cast<META_NS::IObjectInstance>(ret)->GetInstanceId());
388    BASE_NS::string name(namebuf.data(), namebuf.size());
389    META_NS::SetValue(interface_pointer_cast<META_NS::INamed>(ret)->Name(), name);
390    if (flags | META_NS::ICloneableObject::DEEP_CLONE_W_VALUES) {
391        // clone also entity
392        CloneEcs(name, ret);
393    } else {
394        SCENE_PLUGIN_VERBOSE_LOG("Cloned %s", name.c_str());
395    }
396
397    // ensure ecs bindings w/ new object
398    return ret;
399}*/
400
401SCENE_NS::IEcsScene::Ptr NodeImpl::EcsScene() const
402{
403    return ecsScene_.lock();
404}
405
406SCENE_NS::IEcsObject::Ptr NodeImpl::EcsObject() const
407{
408    return ecsObject_;
409}
410
411SceneHolder::Ptr NodeImpl::SceneHolder() const
412{
413    return sceneHolder_.lock();
414}
415
416void NodeImpl::ClaimOwnershipOfEntity(bool ownsEntity)
417{
418    ownsEntity_ = ownsEntity;
419    /*
420    if (!IsResourceClassType()) {
421        // If we do not own the target entity, the name property will be in read-only mode.
422        auto nameInternal = interface_pointer_cast<META_NS::IMetaPropertyInternal>(Name());
423        if (nameInternal) {
424            auto flags = Name()->Flags();
425
426            if (ownsEntity_) {
427                flags = META_NS::SetFlag(flags, META_NS::IMetaProperty::PropertyFlagBits::WRITE);
428            } else {
429                flags = META_NS::ResetFlag(flags, META_NS::IMetaProperty::PropertyFlagBits::WRITE);
430            }
431            nameInternal->SetFlags(flags);
432        }
433    }
434    */
435}
436
437void NodeImpl::RemoveIndex(size_t index)
438{
439    SCENE_PLUGIN_VERBOSE_LOG("remove node from %zu", index);
440    removeIndex_ = index;
441}
442
443void NodeImpl::SetIndex(size_t index)
444{
445    if (removeIndex_ == index) {
446        return;
447    }
448
449    if (removeIndex_ != SIZE_MAX && removeIndex_ < index) {
450        index--;
451    }
452
453    if (auto scene = EcsScene()) {
454        scene->AddEngineTask(MakeTask(
455                                 [index](auto self) {
456                                     if (auto sceneHolder = self->SceneHolder()) {
457                                         sceneHolder->ReindexEntity(self->EcsObject()->GetEntity(), index);
458                                     }
459                                     return false;
460                                 },
461                                 static_pointer_cast<NodeImpl>(GetSelf())),
462            false);
463    }
464    removeIndex_ = SIZE_MAX; // reset, this is not very convenient with subsequent async calls
465    SCENE_PLUGIN_VERBOSE_LOG("move node to %zu", index);
466}
467
468SCENE_NS::IScene::Ptr NodeImpl::GetScene() const
469{
470    return interface_pointer_cast<SCENE_NS::IScene>(ecsScene_);
471}
472
473void NodeImpl::RenameEntity(const BASE_NS::string& name)
474{
475    auto ecsScene = EcsScene();
476    if (!ecsScene) {
477        return;
478    }
479
480    ecsScene->AddEngineTask(MakeTask(
481                                [name](auto self) {
482                                    if (auto sceneHolder = self->SceneHolder()) {
483                                        sceneHolder->RenameEntity(self->EcsObject()->GetEntity(), name);
484                                    }
485                                    return false;
486                                },
487                                static_pointer_cast<NodeImpl>(GetSelf())),
488        false);
489    UpdateChildrenPath();
490}
491
492// Todo, this is assuming moderate size of hierarchy, may have to rethink the recursion if that is not the case
493void NodeImpl::RecursivelyEnableCache(bool isActive)
494{
495    if (auto scene = GetScene()) {
496        scene->SetCacheEnabled(GetSelf<SCENE_NS::INode>(), isActive);
497    }
498
499    if (auto container = GetSelf<META_NS::IContainer>()) {
500        for (auto& object : container->GetAll()) {
501            auto child = static_cast<NodeImpl*>(object.get());
502
503            child->RecursivelyEnableCache(isActive);
504        }
505    }
506}
507
508// Todo, this is assuming moderate size of hierarchy, may have to rethink the recursion if that is not the case
509void NodeImpl::UpdateChildrenPath()
510{
511    if (auto scene=GetScene()) {
512        scene->UpdateCachedNodePath(GetSelf<SCENE_NS::INode>());
513    }
514
515    if (auto container = GetSelf<META_NS::IContainer>()) {
516        auto cachedPath = META_ACCESS_PROPERTY(Path)->GetValue();
517        cachedPath.append(Name()->GetValue());
518        cachedPath.append("/");
519        for (auto& object : container->GetAll()) {
520            auto child = dynamic_cast<NodeImpl*>(object.get());
521            if (child) {
522                child->META_ACCESS_PROPERTY(Path)->SetValue(cachedPath);
523                child->UpdateChildrenPath();
524            }
525        }
526    }
527}
528
529bool NodeImpl::ShouldExport() const
530{
531    bool isProxyNode = false;
532    auto priv = interface_cast<INodeEcsInterfacePrivate>(GetSelf());
533    if (META_NS::Property<uint32_t> lifeCycleInfo = priv->GetLifecycleInfo(false)) {
534        if (lifeCycleInfo->GetValue() == NODE_LC_MIRROR_EXISTING) {
535            isProxyNode = true;
536        }
537    }
538
539    if (!isProxyNode) {
540        isProxyNode = !ownsEntity_;
541    }
542
543    // If we have mesh that needs to be exported, then serialize self.
544    auto mesh = GetMesh();
545    if (auto privateInterface = interface_cast<INodeEcsInterfacePrivate>(mesh)) {
546        if (privateInterface->ShouldExport()) {
547            return true;
548        }
549    }
550
551    // Proxy node is exported if ..
552    if (isProxyNode) {
553        // It has changed properties.
554        if (HasChangedProperties(*GetSelf<META_NS::IMetadata>())) {
555            return true;
556        }
557
558        // It has attachments.
559        if (auto attach = GetSelf<META_NS::IAttach>()) {
560            if (attach->GetAttachments({}, false).size() > 0) {
561                return true;
562            }
563        }
564
565        // Any of its child needs to be exported.
566        if (auto container = GetSelf<META_NS::IContainer>()) {
567            for (auto& childNode : container->GetAll<INode>()) {
568                if (auto privateInterface = interface_cast<INodeEcsInterfacePrivate>(childNode)) {
569                    if (privateInterface->ShouldExport()) {
570                        return true;
571                    }
572                }
573            }
574        }
575
576        return false;
577    }
578
579    return true;
580}
581
582bool NodeImpl::Initialize(SCENE_NS::IEcsScene::Ptr& scene, SCENE_NS::IEcsObject::Ptr& ecsObject,
583    SCENE_NS::INode::Ptr parent, const BASE_NS::string& path, const BASE_NS::string& name,
584    SceneHolder::WeakPtr sceneHolder, CORE_NS::Entity entity)
585{
586    if (!ecsObject || !scene) {
587        return false;
588    }
589
590    ecsScene_ = scene;
591    ecsObject_ = ecsObject;
592    sceneHolder_ = sceneHolder;
593
594    auto currentParent = GetParent();
595    if (!currentParent && parent) {
596        interface_cast<IContainer>(parent)->Add(GetSelf());
597    }
598
599    META_ACCESS_PROPERTY(ConnectionStatus)->SetBind(ecsObject->ConnectionStatus());
600
601    // Need to set a path before ECS instantiation to enable IContainer to share a proxy beforehand
602    SetPath(path, name, entity);
603    if (!ecsObject->GetEcs()) {
604        // We will not proceed further with construction until we have esc object bound
605        return false;
606    }
607
608    return true; // CompleteInitialization(path);
609}
610
611void NodeImpl::BindObject(SCENE_NS::INode::Ptr node)
612{
613    auto priv = interface_cast<INodeEcsInterfacePrivate>(node);
614    if (!priv) {
615        return;
616    }
617
618    if (priv->EcsObject()) {
619        return;
620    }
621
622    bool create = false;
623
624    if (META_NS::Property<uint32_t> creationPolicy = priv->GetLifecycleInfo()) {
625        create = (creationPolicy->GetValue() == NODE_LC_CREATED);
626    }
627
628    SCENE_PLUGIN_VERBOSE_LOG(
629        "Connecting object from property: %s", (GetValue(node->Path()) + GetValue(node->Name())).c_str());
630    EcsScene()->BindNodeToEcs(node, GetValue(node->Path()) + GetValue(node->Name()), create);
631}
632
633bool NodeImpl::CompleteInitialization(const BASE_NS::string& path)
634{
635    initializeTaskToken_ = {};
636
637    if (auto scene = EcsScene()) {
638        auto meta = interface_pointer_cast<META_NS::IMetadata>(ecsObject_);
639
640        if (auto name = Name()->GetValue(); name != path) {
641            RenameEntity(name);
642        }
643
644        // Ensure that our cache path is correct now that we have entity connected.
645        interface_cast<SCENE_NS::IScene>(scene)->UpdateCachedNodePath(GetSelf<SCENE_NS::INode>());
646
647        propHandler_.Reset();
648        propHandler_.SetSceneHolder(SceneHolder());
649
650        // Use node properties as default, in case we have created the entity to ECS.
651        propHandler_.SetUseEcsDefaults(!ownsEntity_);
652
653        BindChanges<BASE_NS::Math::Quat>(propHandler_, META_ACCESS_PROPERTY(Rotation), meta, TRANSFORM_ROTATION);
654        BindChanges<BASE_NS::Math::Vec3>(propHandler_, META_ACCESS_PROPERTY(Position), meta, TRANSFORM_POSITION);
655        BindChanges<BASE_NS::Math::Vec3>(propHandler_, META_ACCESS_PROPERTY(Scale), meta, TRANSFORM_SCALE);
656        BindChanges<bool>(propHandler_, META_ACCESS_PROPERTY(Visible), meta, NODE_ENABLED);
657        BindChanges<uint64_t>(propHandler_, META_ACCESS_PROPERTY(LayerMask), meta, LAYER_MASK);
658
659        // Restore default behavior.
660        propHandler_.SetUseEcsDefaults(true);
661
662        BindChanges<BASE_NS::Math::Mat4X4>(propHandler_, META_ACCESS_PROPERTY(LocalMatrix), meta, LMATRIX_MATRIX);
663
664        auto rh = meta->GetPropertyByName(RM_HANDLE);
665
666        auto mesh = META_NS::GetValue(Mesh());
667        if (mesh) {
668            // if se have a mesh set, initialize it
669            InitializeMesh(mesh, GetSelf<IEcsObject>());
670        } else if (rh) {
671            // otherwise get mesh from engine only if the component has render mesh present
672            GetMeshFromEngine();
673        }
674
675        // if the node has render mesh component, subscribe changes from engin
676        if (rh) {
677            rh->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() { GetMeshFromEngine(); }),
678                reinterpret_cast<uint64_t>(this));
679        }
680
681        // and set the proxy if user wants to change the mesh through the property
682        Mesh()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
683            if (auto node = interface_pointer_cast<SCENE_NS::INode>(META_NS::GetValue(Mesh()))) {
684                node->Status()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(
685                    [](const auto& self, const auto& status) {
686                        if (self && status && status->GetValue() == SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED) {
687                            static_cast<NodeImpl*>(self.get())->SetMeshToEngine();
688                        }
689                    },
690                    GetSelf(), node->Status()));
691                if (auto status = META_NS::GetValue(node->Status());
692                    status == SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED ||
693                    status == SCENE_NS::INode::NODE_STATUS_CONNECTED) {
694                    SetMeshToEngine();
695                }
696            }
697        }),
698            reinterpret_cast<uint64_t>(this));
699
700        if (buildBehavior_ == NODE_BUILD_CHILDREN_GRADUAL) {
701            // We are assumingly on the correct thread already, but have deriving classes a slot to complete
702            // initialization Before notifying the client
703            scene->AddApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(
704                                          [buildBehavior = buildBehavior_](const auto& node) {
705                                              if (node) {
706                                                  node->BuildChildren(buildBehavior);
707                                              }
708                                              return false;
709                                          },
710                                          GetSelf<SCENE_NS::INode>()),
711                true);
712        }
713
714        if (auto sceneHolder = SceneHolder()) {
715            for (auto& attachment : ecsObject_->GetAttachments()) {
716                if (auto animation = sceneHolder->GetAnimation(attachment)) {
717                    // Make flat animation array based on name-property + entity id separated by ':'
718                    // This is identical to meshes and materials, but the implementation is tad different
719                    auto name = META_NS::GetValue(interface_cast<META_NS::INamed>(animation)->Name());
720                    name.append(":");
721                    name.append(BASE_NS::to_hex(attachment.id));
722                    if (auto attachment = GetScene()->GetAnimation(name)) {
723                        if (auto typed = interface_cast<META_NS::IAttachment>(attachment)) {
724                            Attach(interface_pointer_cast<IObject>(attachment), GetSelf());
725                        }
726                    }
727                }
728            }
729        }
730
731        SubscribeToNameChanges();
732
733        return true;
734    }
735    return false;
736}
737
738void NodeImpl::SetPathWithEcsNode(const BASE_NS::shared_ptr<NodeImpl>& self, const BASE_NS::string& name,
739    SceneHolder::Ptr sceneHolder, const CORE3D_NS::ISceneNode* ecsNode)
740{
741    BASE_NS::weak_ptr<IObject> me = static_pointer_cast<IObject>(self);
742    if (!self->EcsObject()->GetEcs()) {
743        // Initialize ECS Object
744        SCENE_PLUGIN_VERBOSE_LOG("binding node: %s", name.c_str());
745        // We enable layer component just to have it handled consistently across
746        // the other properties, this may not be desired
747        auto entity = ecsNode->GetEntity();
748        sceneHolder->EnableLayerComponent(entity);
749
750        auto ecsObject = self->EcsObject();
751        // Introspect the engine components
752        if (auto proxyIf = interface_cast<SCENE_NS::IEcsProxyObject>(ecsObject)) {
753            proxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
754        }
755        ecsObject->DefineTargetProperties(self->PropertyNameMask());
756        ecsObject->SetEntity(sceneHolder->GetEcs(), entity);
757        sceneHolder->UpdateAttachments(ecsObject);
758        sceneHolder->QueueApplicationTask(
759            MakeTask(
760                [name](auto selfObject) {
761                    if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
762                        self->CompleteInitialization(name);
763                        self->SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTED);
764                        if (auto node = interface_cast<SCENE_NS::INode>(selfObject)) {
765                            META_NS::Invoke<META_NS::IOnChanged>(node->OnLoaded());
766                        }
767                    }
768                    return false;
769                },
770                me),
771            false);
772    } else {
773        // ECS was already initialized, presumably we were just re-parented, update index
774        sceneHolder->QueueApplicationTask(MakeTask(
775                                              [](auto selfObject) {
776                                                  if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
777                                                      self->UpdateChildrenPath();
778                                                  }
779                                                  return false;
780                                              },
781                                              me),
782            false);
783    }
784}
785
786bool NodeImpl::SetPathWithoutNode(const BASE_NS::shared_ptr<NodeImpl>& self, const BASE_NS::string& name,
787    const BASE_NS::string& fullPath, SceneHolder::Ptr sceneHolder)
788{
789    CORE_NS::Entity entity;
790    BASE_NS::weak_ptr<IObject> me = static_pointer_cast<IObject>(self);
791    if (sceneHolder->FindResource(name, fullPath, entity)) {
792        SCENE_PLUGIN_VERBOSE_LOG("binding resource: %s", name.c_str());
793        if (auto proxyIf = interface_cast<SCENE_NS::IEcsProxyObject>(self->EcsObject())) {
794            proxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
795        }
796        self->EcsObject()->DefineTargetProperties(self->PropertyNameMask());
797        self->EcsObject()->SetEntity(sceneHolder->GetEcs(), entity);
798        sceneHolder->QueueApplicationTask(MakeTask(
799                                              [name](auto selfObject) {
800                                                  if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
801                                                      // There is not much to bind inside node, could presumably
802                                                      // fork this as own instance
803                                                      self->CompleteInitialization(name);
804                                                      self->SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTED);
805                                                      if (self->buildBehavior_ == NODE_BUILD_CHILDREN_ALL) {
806                                                          self->BuildChildren(self->buildBehavior_);
807                                                      }
808                                                      META_NS::Invoke<META_NS::IOnChanged>(self->OnLoaded());
809                                                  }
810                                                  return false;
811                                              },
812                                              me),
813            false);
814    } else if (META_NS::GetValue(self->GetScene()->Asynchronous()) && self->shouldRetry_) {
815        // When nodes are deserialized, it is possible that the construction order
816        // prepares children before their parent, just letting the task spin once more
817        // fixes the problem. ToDo: Get more elegant solution for this
818        self->shouldRetry_ = false;
819        return true;
820    } else {
821        // Giving in. If the node arrives later, it may be re-attempted due e.g. parent
822        // or name changes, otherwise we are done with it.
823        self->shouldRetry_ = true;
824        sceneHolder->QueueApplicationTask(MakeTask(
825                                              [name](auto selfObject) {
826                                                  if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
827                                                      self->SetStatus(SCENE_NS::INode::NODE_STATUS_DISCONNECTED);
828                                                  }
829                                                  return false;
830                                              },
831                                              me),
832            false);
833    }
834    return false;
835}
836
837// It is turning out that the path is pretty much the only thing that we can rely when operating asynchronously
838// from application thread to engine thread. Still this can go wrong if subsequent events are not recorded in
839// order.
840void NodeImpl::SetPath(const BASE_NS::string& path, const BASE_NS::string& name, CORE_NS::Entity entity)
841{
842    // BASE_NS::string name;
843
844    META_ACCESS_PROPERTY(Path)->SetValue(path);
845    META_ACCESS_PROPERTY(Name)->SetValue(name);
846
847    // SCENE_PLUGIN_VERBOSE_LOG("asking engine to make sure that: '%s' is child of '%s'", name.c_str(),
848    if (auto scene = EcsScene()) {
849        SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTING);
850        if (auto iscene = interface_cast<SCENE_NS::IScene>(scene)) {
851            iscene->UpdateCachedNodePath(GetSelf<SCENE_NS::INode>());
852        }
853
854        initializeTaskToken_ = scene->AddEngineTask(
855            MakeTask(
856                [name, fullpath = path + name](auto selfObject) {
857                    if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
858                        if (auto sceneHolder = self->SceneHolder()) {
859                            auto parentPath = self->META_ACCESS_PROPERTY(Path)
860                                                  ->GetValue(); // should also this be snapshot from App thread?
861
862                            auto ecsNode = sceneHolder->ReparentEntity(parentPath, name);
863
864                            if (ecsNode) {
865                                SetPathWithEcsNode(self, name, sceneHolder, ecsNode);
866                            } else {
867                                // The entity might be valid even if there is no node attached
868                                return SetPathWithoutNode(self, name, fullpath, sceneHolder);
869                            }
870                        }
871                    }
872                    return false;
873                },
874                GetSelf()),
875            false);
876    }
877}
878
879// static constexpr BASE_NS::string_view LIFECYCLE_PROPERTY_NAME = "NodeLifeCycle";
880
881bool NodeImpl::HasLifecycleInfo()
882{
883    return GetLifecycleInfo(false) != nullptr;
884}
885
886// Different scenarios currently to be considered
887// 1) Create and bind a new prefab instance
888// -> create and assign ecs node
889// -> create new prefab instance
890// -> bind (read data from prefab)
891// prefab and its children need to know they were generated by prefab
892//
893// 2) Bind a new node to an existing entity:
894// this can take place throud two different routes, either the node is explicitly requested from the scene or
895// restored by de-serialization/import/clone
896// -> just prepare and assign ecs object
897// -> system will try find and sync the state later (potentially bidirectional sync)
898// values that are modified should have set-bits on properties
899//
900// 3) Create new proxy with entity and component with default values:
901// this is either through de-serialization or c++ api
902// -> sync the proxy state to ecs
903// information on entity creation needs to be preserved
904
905META_NS::IProperty::Ptr NodeImpl::GetLifecycleInfo(bool create)
906{
907    META_NS::IProperty::Ptr creationPolicy;
908    if (auto meta = GetSelf<META_NS::IMetadata>()) {
909        META_NS::IProperty::Ptr lifeCycle;
910        if (lifeCycle = meta->GetPropertyByName(LIFECYCLE_PROPERTY_NAME); !lifeCycle && create) {
911            auto p = META_NS::ConstructProperty<uint32_t>(LIFECYCLE_PROPERTY_NAME, 0, META_NS::DEFAULT_PROPERTY_FLAGS | META_NS::ObjectFlagBits::INTERNAL);
912            meta->AddProperty(p);
913            lifeCycle = p;
914        }
915
916        if (creationPolicy = META_NS::Property<uint32_t>(lifeCycle); !creationPolicy && create) {
917            CORE_LOG_E("%s: inconsistent system state. Can not store creation info for node", __func__);
918        }
919    }
920    return creationPolicy;
921}
922
923void NodeImpl::EnsureEcsBinding(SCENE_NS::IEcsScene::Ptr scene, bool force)
924{
925    SCENE_PLUGIN_VERBOSE_LOG("Node::EnsureEcsBinding called for %s (%s)", GetName().c_str(), Path()->Get().c_str());
926    if (scene && (force || !EcsScene())) {
927        BASE_NS::string fullPath = META_NS::GetValue(META_ACCESS_PROPERTY(Path));
928        fullPath.append(META_NS::GetValue(Name()));
929        auto self = GetSelf<SCENE_NS::INode>();
930
931        INodeEcsInterfacePrivate* privateParts { nullptr };
932
933	bool shouldCreate = true;
934        privateParts = interface_cast<INodeEcsInterfacePrivate>(self);
935        if (auto lifeCycleInfo = privateParts->GetLifecycleInfo(false)) {
936           if (auto value = META_NS::Property<uint32_t>(lifeCycleInfo)->GetValue()) {
937                shouldCreate = (value & NODE_LC_CLONED) || (value & NODE_LC_CREATED);
938            }
939        }
940        scene->BindNodeToEcs(self, fullPath, shouldCreate);
941    }
942}
943
944BASE_NS::vector<SCENE_NS::IProxyObject::PropertyPair> NodeImpl::ListBoundProperties() const
945{
946    return propHandler_.GetBoundProperties();
947}
948
949void NodeImpl::BuildChildrenIterateOver(const BASE_NS::shared_ptr<NodeImpl>& self, const SCENE_NS::IEcsScene::Ptr& ecs,
950    const BASE_NS::string& fullPath, BASE_NS::array_view<CORE3D_NS::ISceneNode* const> children)
951{
952    BASE_NS::weak_ptr<IObject> me = static_pointer_cast<IObject>(self);
953    for (const auto& child : children) {
954        auto childPath = fullPath;
955        auto childName = child->GetName();
956        if (!childName.empty() && childName.find("/") == BASE_NS::string_view::npos &&
957            !childName.starts_with(MULTI_MESH_CHILD_PREFIX)) {
958            childPath.append("/");
959            childPath.append(childName);
960
961            ecs->AddApplicationTask(
962                MakeTask(
963                    [childPath, childName](auto selfObject) {
964                        if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
965                            if (auto scene = self->GetScene()) {
966                                if (auto child = interface_pointer_cast<SCENE_NS::INode>(self->FindByName(childName))) {
967                                    self->MonitorChild(child);
968                                    // The call is pretty much costless when the ecs object has been already set, not
969                                    // sure if there is a code path that needs this, though
970                                    static_pointer_cast<NodeImpl>(child)->EnsureEcsBinding(self->EcsScene());
971                                } else {
972                                    auto node = scene->GetNode(childPath, META_NS::IObject::UID, self->buildBehavior_);
973                                    self->MonitorChild(node);
974                                    node->BuildChildren(self->buildBehavior_);
975                                }
976                            }
977                        }
978                        return false;
979                    },
980                    me),
981                false);
982        }
983    }
984}
985
986bool NodeImpl::BuildChildren(SCENE_NS::INode::BuildBehavior options)
987{
988    buildBehavior_ = options;
989
990    if (options == SCENE_NS::INode::NODE_BUILD_CHILDREN_NO_BUILD || !GetScene()) {
991        return true;
992    }
993
994    if (!ecsObject_ || !ecsObject_->GetEcs()) {
995        if (!META_NS::GetValue(GetScene()->Asynchronous())) {
996            CORE_LOG_W("%s: no ecs available, cannot inspect children", __func__);
997        }
998        return false;
999    }
1000
1001    if (auto scene = EcsScene()) {
1002        BASE_NS::string fullPath = META_ACCESS_PROPERTY(Path)->GetValue();
1003        fullPath.append(Name()->GetValue());
1004
1005        if (fullPath.starts_with("/")) {
1006            fullPath.erase(0, 1);
1007        }
1008        if (options == SCENE_NS::INode::NODE_BUILD_ONLY_DIRECT_CHILDREN) {
1009            // yeah, for now.. let's just do this.
1010            options = SCENE_NS::INode::NODE_BUILD_CHILDREN_GRADUAL;
1011        }
1012
1013        scene->AddEngineTask(MakeTask([me = BASE_NS::weak_ptr(GetSelf()), fullPath]() {
1014            if (auto self = static_pointer_cast<NodeImpl>(me.lock())) {
1015                if (auto ecs = self->EcsScene()) {
1016                    CORE3D_NS::INodeSystem* nodeSystem =
1017                        static_cast<CORE3D_NS::INodeSystem*>(ecs->GetEcs()->GetSystem(CORE3D_NS::INodeSystem::UID));
1018                    const auto& root = nodeSystem->GetRootNode();
1019                    const auto& node = root.LookupNodeByPath(fullPath);
1020                    if (!node) {
1021                        CORE_LOG_W("%s: cannot find '%s'", __func__, fullPath.c_str());
1022                    } else {
1023                        // 1) on engine queue introspect children nodes
1024                        const auto& children = node->GetChildren();
1025                        if (children.size() == 0 &&
1026                            META_NS::GetValue(self->Status()) != SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED) {
1027                            ecs->AddApplicationTask(MakeTask(
1028                                                        [](auto selfObject) {
1029                                                            if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
1030                                                                self->SetStatus(
1031                                                                    SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED);
1032                                                                META_NS::Invoke<META_NS::IOnChanged>(self->OnBound());
1033                                                                self->bound_ = true;
1034                                                            }
1035                                                            return false;
1036                                                        },
1037                                                        me),
1038                                false);
1039                        } else {
1040                            // 2) on app queue instantiate nodes
1041                            BuildChildrenIterateOver(self, ecs, fullPath, children);
1042
1043                            // once we have iterated through children, check status once
1044                            ecs->AddApplicationTask(MakeTask(
1045                                                        [](auto selfObject) {
1046                                                            if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
1047                                                                self->CheckChildrenStatus();
1048                                                            }
1049                                                            return false;
1050                                                        },
1051                                                        me),
1052                                false);
1053                        }
1054                    }
1055                }
1056            }
1057            return false;
1058        }),
1059            false);
1060    }
1061    return true;
1062}
1063
1064void NodeImpl::CheckChildrenStatus()
1065{
1066    for (const auto& child : monitored_) {
1067        if (!child->Ready()) {
1068            return;
1069        }
1070    }
1071    size_t ix = 0;
1072    while (ix < monitored_.size()) {
1073        if (monitored_[ix]->Valid()) {
1074            ix++;
1075        } else {
1076            monitored_.erase(monitored_.begin() + ix);
1077        }
1078    }
1079
1080    if (META_NS::GetValue(Status()) != SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED) {
1081        SetStatus(SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED);
1082    }
1083
1084    if (!bound_) {
1085        SCENE_PLUGIN_VERBOSE_LOG("%s: all children completed", META_NS::GetValue(Name()).c_str());
1086        META_NS::Invoke<META_NS::IOnChanged>(OnBound());
1087    }
1088
1089    bound_ = true;
1090}
1091
1092BASE_NS::Math::Mat4X4 NodeImpl::GetGlobalTransform() const
1093{
1094    auto ret = BASE_NS::Math::IDENTITY_4X4;
1095    BASE_NS::vector<META_NS::IContainable::Ptr> nodes;
1096    nodes.push_back(GetSelf<META_NS::IContainable>());
1097    auto current = nodes.back()->GetParent();
1098    while (auto containable = interface_pointer_cast<META_NS::IContainable>(current)) {
1099        nodes.push_back(containable);
1100        current = nodes.back()->GetParent();
1101    }
1102
1103    for (size_t ix = nodes.size(); ix != 0; ix--) {
1104        ret = ret * META_NS::GetValue(interface_pointer_cast<INode>(nodes[ix - 1])->LocalMatrix());
1105    }
1106
1107    return ret;
1108}
1109
1110void NodeImpl::SetGlobalTransform(const BASE_NS::Math::Mat4X4& mat)
1111{
1112    auto global = BASE_NS::Math::IDENTITY_4X4;
1113    BASE_NS::vector<META_NS::IContainable::Ptr> nodes;
1114    nodes.push_back(GetSelf<META_NS::IContainable>());
1115
1116    auto current = nodes.back()->GetParent();
1117    while (auto containable = interface_pointer_cast<META_NS::IContainable>(current)) {
1118        nodes.push_back(containable);
1119        current = nodes.back()->GetParent();
1120    }
1121
1122    for (size_t ix = nodes.size(); ix != 1; ix--) {
1123        global = global * META_NS::GetValue(interface_pointer_cast<INode>(nodes[ix - 1])->LocalMatrix());
1124    }
1125
1126    // META_NS::SetValue(LocalMatrix(), mat / global);
1127    auto newLocal = BASE_NS::Math::Inverse(global) * mat;
1128    BASE_NS::Math::Quat rotation;
1129    BASE_NS::Math::Vec3 scale;
1130    BASE_NS::Math::Vec3 translate;
1131    BASE_NS::Math::Vec3 skew;
1132    BASE_NS::Math::Vec4 persp;
1133    if (BASE_NS::Math::Decompose(newLocal, scale, rotation, translate, skew, persp)) {
1134        META_NS::SetValue(Scale(), scale);
1135        META_NS::SetValue(Rotation(), rotation);
1136        META_NS::SetValue(Position(), translate);
1137    }
1138}
1139
1140void NodeImpl::MonitorChild(SCENE_NS::INode::Ptr node)
1141{
1142    for (auto& child : monitored_) {
1143        if (*child == node) {
1144            return;
1145        }
1146    }
1147
1148    monitored_.emplace_back(NodeMonitor::Create(node, *this));
1149    bound_ = false;
1150}
1151
1152void NodeImpl::SetMeshToEngine()
1153{
1154    if (auto mesh = META_NS::GetValue(Mesh())) {
1155        if (auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(mesh)) {
1156            if (auto scene = EcsScene()) {
1157                scene->AddEngineTask(
1158                    MakeTask(
1159                        [](const auto self, const auto meshObject) {
1160                            if (auto sceneHolder = static_cast<NodeImpl*>(self.get())->SceneHolder()) {
1161                                sceneHolder->SetMesh(self->GetEntity(), meshObject->GetEntity());
1162                            }
1163                            return false;
1164                        },
1165                        GetSelf<SCENE_NS::IEcsObject>(), interface_pointer_cast<SCENE_NS::IEcsObject>(mesh)),
1166                    false);
1167            }
1168        }
1169    }
1170}
1171
1172void NodeImpl::SetMesh(SCENE_NS::IMesh::Ptr mesh)
1173{
1174    if (mesh == META_NS::GetValue(Mesh())) {
1175        return; // the matching mesh is already set
1176    }
1177
1178    META_NS::SetValue(Mesh(), mesh);
1179}
1180
1181SCENE_NS::IMultiMeshProxy::Ptr NodeImpl::CreateMeshProxy(size_t count, SCENE_NS::IMesh::Ptr mesh)
1182{
1183    // SCENE_NS::IMultiMeshProxy::Ptr ret { SCENE_NS::MultiMeshProxy() };
1184    SCENE_NS::IMultiMeshProxy::Ptr ret =
1185        GetObjectRegistry().Create<SCENE_NS::IMultiMeshProxy>(SCENE_NS::ClassId::MultiMeshProxy);
1186
1187    interface_cast<IMultimeshInitilization>(ret)->Initialize(SceneHolder(), count, EcsObject()->GetEntity());
1188
1189    if (mesh) {
1190        ret->Mesh()->SetValue(mesh);
1191    }
1192
1193    // return
1194    SetMultiMeshProxy(ret);
1195    return ret;
1196}
1197
1198void NodeImpl::SetMultiMeshProxy(SCENE_NS::IMultiMeshProxy::Ptr multimesh)
1199{
1200    multimesh_ = multimesh;
1201}
1202
1203SCENE_NS::IMultiMeshProxy::Ptr NodeImpl::GetMultiMeshProxy() const
1204{
1205    return multimesh_;
1206}
1207
1208void NodeImpl::ReleaseEntityOwnership()
1209{
1210    if (!ecsObject_) {
1211        return;
1212    }
1213
1214    auto entity = ecsObject_->GetEntity();
1215    if (!CORE_NS::EntityUtil::IsValid(entity)) {
1216        return;
1217    }
1218
1219    auto sh = SceneHolder();
1220    if (sh) {
1221        auto attachments = ecsObject_->GetAttachments();
1222        for (auto attachment : attachments) {
1223            ecsObject_->RemoveAttachment(attachment);
1224        }
1225    }
1226
1227    if (sh) {
1228        sh->SetEntityActive(entity, true);
1229
1230        if (ownsEntity_) {
1231            sh->DestroyEntity(entity);
1232        }
1233
1234        SetEntity(ecsObject_->GetEcs(), {});
1235    }
1236}
1237
1238void NodeImpl::Destroy()
1239{
1240    UnsubscribeFromNameChanges();
1241
1242    META_NS::GetObjectRegistry().Purge();
1243
1244    if (Name()) {
1245        SCENE_PLUGIN_VERBOSE_LOG(
1246            "Tearing down: %s%s", META_NS::GetValue(Path()).c_str(), META_NS::GetValue(Name()).c_str());
1247        if (auto ecss = ecsScene_.lock()) {
1248            ecss->CancelEngineTask(initializeTaskToken_);
1249        }
1250    }
1251
1252    ReleaseEntityOwnership();
1253    Fwd::Destroy();
1254}
1255
1256SCENE_NS::IPickingResult::Ptr NodeImpl::GetWorldMatrixComponentAABB(bool isRecursive) const
1257{
1258    auto ret = objectRegistry_->Create<SCENE_NS::IPickingResult>(SCENE_NS::ClassId::PendingVec3Request);
1259    if (ret && SceneHolder()) {
1260        SceneHolder()->QueueEngineTask(
1261            META_NS::MakeCallback<META_NS::ITaskQueueTask>([isRecursive, weakRet = BASE_NS::weak_ptr(ret),
1262                                                           weakSh = sceneHolder_,
1263                                                           weakSelf = BASE_NS::weak_ptr(ecsObject_)]() {
1264                if (auto sh = weakSh.lock()) {
1265                    if (auto ret = weakRet.lock()) {
1266                        if (auto self = weakSelf.lock()) {
1267                            if (sh->GetWorldMatrixComponentAABB(ret, self->GetEntity(), isRecursive)) {
1268                                sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet]() {
1269                                    if (auto writable =
1270                                            interface_pointer_cast<SCENE_NS::IPendingRequestData<BASE_NS::Math::Vec3>>(
1271                                                weakRet)) {
1272                                        writable->MarkReady();
1273                                    }
1274                                    return false;
1275                                }),
1276                                    false);
1277                            }
1278                        }
1279                    }
1280                }
1281                return false;
1282            }),
1283            false);
1284        return ret;
1285    }
1286    return SCENE_NS::IPickingResult::Ptr();
1287}
1288
1289SCENE_NS::IPickingResult::Ptr NodeImpl::GetTransformComponentAABB(bool isRecursive) const
1290{
1291    auto ret = objectRegistry_->Create<SCENE_NS::IPickingResult>(SCENE_NS::ClassId::PendingVec3Request);
1292    if (ret && SceneHolder()) {
1293        SceneHolder()->QueueEngineTask(
1294            META_NS::MakeCallback<META_NS::ITaskQueueTask>([isRecursive, weakRet = BASE_NS::weak_ptr(ret),
1295                                                           weakSh = sceneHolder_,
1296                                                           weakSelf = BASE_NS::weak_ptr(ecsObject_)]() {
1297                if (auto sh = weakSh.lock()) {
1298                    if (auto ret = weakRet.lock()) {
1299                        if (auto self = weakSelf.lock()) {
1300                            if (sh->GetTransformComponentAABB(ret, self->GetEntity(), isRecursive)) {
1301                                sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet]() {
1302                                    if (auto writable =
1303                                            interface_pointer_cast<SCENE_NS::IPendingRequestData<BASE_NS::Math::Vec3>>(
1304                                                weakRet)) {
1305                                        writable->MarkReady();
1306                                    }
1307                                    return false;
1308                                }),
1309                                    false);
1310                            }
1311                        }
1312                    }
1313                }
1314                return false;
1315            }),
1316            false);
1317        return ret;
1318    }
1319    return SCENE_NS::IPickingResult::Ptr();
1320}
1321
1322void NodeImpl::SetStatus(SCENE_NS::INode::NodeStatus status)
1323{
1324    META_ACCESS_PROPERTY(Status)->SetValue(status);
1325}
1326/*
1327bool NodeImpl::Export(
1328    META_NS::Serialization::IExportContext& context, META_NS::Serialization::ClassPrimitive& value) const
1329{
1330    if (!ShouldExport()) {
1331        return false;
1332    }
1333
1334    SCENE_PLUGIN_VERBOSE_LOG("NodeImpl::%s %s", __func__, Name()->Get().c_str());
1335    return Fwd::Export(context, value);
1336}
1337*/
1338void NodeImpl::SetSuperInstance(const IObject::Ptr& aggr, const IObject::Ptr& super)
1339{
1340    Fwd::SetSuperInstance(aggr, super);
1341    containable_ = interface_cast<IContainable>(super);
1342    mutableContainable_ = interface_cast<IMutableContainable>(super);
1343}
1344
1345void NodeImpl::SetParent(const IObject::Ptr& parent)
1346{
1347    if (mutableContainable_) {
1348        mutableContainable_->SetParent(parent);
1349    } else {
1350        parent_ = parent;
1351    }
1352}
1353
1354META_NS::IObject::Ptr NodeImpl::GetParent() const
1355{
1356    if (containable_) {
1357        return containable_->GetParent();
1358    }
1359
1360    auto parent = parent_.lock();
1361    if (parent) {
1362        return parent;
1363    }
1364
1365    return {};
1366}
1367
1368SCENE_NS::IMesh::Ptr NodeImpl::GetMesh() const
1369{
1370    return META_NS::GetValue(Mesh());
1371}
1372
1373void NodeImpl::InitializeMesh(
1374    const SCENE_NS::IMesh::Ptr& mesh, const BASE_NS::shared_ptr<NodeImpl>& node, const BASE_NS::string_view entityName)
1375{
1376    auto scene = node->EcsScene();
1377    if (auto ecsObject = META_NS::GetObjectRegistry().Create<SCENE_NS::IEcsObject>(SCENE_NS::ClassId::EcsObject)) {
1378        if (auto privateInit = interface_cast<INodeEcsInterfacePrivate>(mesh)) {
1379            privateInit->Initialize(scene, ecsObject, {}, "", BASE_NS::string(entityName), node->SceneHolder(), {});
1380        }
1381    }
1382}
1383
1384void NodeImpl::InitializeMesh(const SCENE_NS::IMesh::Ptr& mesh, const BASE_NS::shared_ptr<SCENE_NS::IEcsObject>& self)
1385{
1386    auto strongMe = interface_pointer_cast<IObject>(self);
1387    if (auto node = static_pointer_cast<NodeImpl>(strongMe)) {
1388        if (auto sceneHolder = node->SceneHolder()) {
1389            auto entityName = sceneHolder->GetMeshName(self->GetEntity());
1390            if (!entityName.empty()) {
1391                sceneHolder->QueueApplicationTask(MakeTask(
1392                                                      [entityName, mesh](auto selfObject) {
1393                                                          if (auto node = static_pointer_cast<NodeImpl>(selfObject)) {
1394                                                              InitializeMesh(mesh, node, entityName);
1395                                                          }
1396                                                          return false;
1397                                                      },
1398                                                      strongMe),
1399                    false);
1400            }
1401        }
1402    }
1403}
1404
1405SCENE_NS::IMesh::Ptr NodeImpl::GetMeshFromEngine()
1406{
1407    SCENE_NS::IMesh::Ptr ret {};
1408    if (auto iscene = GetScene()) {
1409        if (META_NS::GetValue(iscene->Asynchronous()) == false) {
1410            auto entityName = SceneHolder()->GetMeshName(EcsObject()->GetEntity());
1411            if (!entityName.empty()) {
1412                ret = iscene->GetMesh(entityName);
1413            }
1414        }
1415    }
1416
1417    if (!ret) {
1418        ret = GetObjectRegistry().Create<SCENE_NS::IMesh>(SCENE_NS::ClassId::Mesh);
1419
1420        if (auto scene = EcsScene()) {
1421            scene->AddEngineTask(MakeTask(
1422                                     [ret](auto selfObject) {
1423                                         if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfObject)) {
1424                                             InitializeMesh(ret, self);
1425                                         }
1426                                         return false;
1427                                     },
1428                                     GetSelf()),
1429                false);
1430        }
1431    }
1432    if (auto node = interface_cast<SCENE_NS::INode>(ret)) {
1433        if (META_NS::GetValue(node->Status()) != SCENE_NS::INode::NODE_STATUS_DISCONNECTED) {
1434            META_NS::SetValue(Mesh(), ret);
1435        }
1436    }
1437
1438    return ret;
1439}
1440
1441using PropertyNameCriteria = BASE_NS::unordered_map<BASE_NS::string_view, BASE_NS::vector<BASE_NS::string_view>>;
1442PropertyNameCriteria& NodeImpl::PropertyNameMask()
1443{
1444    return ecs_property_names_;
1445}
1446
1447META_NS::IObject::Ptr NodeImpl::Resolve(const META_NS::RefUri& uri) const
1448{
1449    auto p = Super::Resolve(uri);
1450    if (p) {
1451        return p;
1452    }
1453
1454    INode::Ptr current = GetSelf<INode>();
1455
1456    META_NS::RefUri ref { uri.RelativeUri() };
1457    if (ref.IsEmpty()) {
1458        return interface_pointer_cast<META_NS::IObject>(current);
1459    }
1460
1461    if (ref.StartsFromRoot()) {
1462        ref.SetStartsFromRoot(false);
1463        auto obj = interface_pointer_cast<META_NS::IObject>(GetScene()->RootNode()->GetValue());
1464        return obj ? obj->Resolve(BASE_NS::move(ref)) : nullptr;
1465    }
1466
1467    return {}; // ResolveSegment(current, BASE_NS::move(ref));
1468}
1469
1470SCENE_BEGIN_NAMESPACE()
1471void RegisterNodeImpl()
1472{
1473    META_NS::GetObjectRegistry().RegisterObjectType<NodeImpl>();
1474}
1475void UnregisterNodeImpl()
1476{
1477    META_NS::GetObjectRegistry().UnregisterObjectType<NodeImpl>();
1478}
1479SCENE_END_NAMESPACE()
1480