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 
19 using 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 
HasChangedProperties(META_NS::IMetadata & meta)27 bool 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 
44 namespace {
45 
GetChildIndex(SCENE_NS::INode & parent,SCENE_NS::INode & child)46 size_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
GetInterface(const BASE_NS::Uid & uid) const67 const 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 
GetInterface(const BASE_NS::Uid & uid)76 CORE_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 
GetAttachedState() const85 SCENE_NS::NodeState NodeImpl::GetAttachedState() const
86 {
87     return attachedState_;
88 }
89 
IsConnected()90 bool 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 
DisableInputHandling()99 void 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 
Connect(const INode::Ptr & parent)110 bool 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 
Activate()139 void 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 
Deactivate()167 void 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 
AttachToHierarchy()185 void 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 
DetachFromHierarchy()202 void 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 
SubscribeToNameChanges()219 void 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 
UnsubscribeFromNameChanges()230 void NodeImpl::UnsubscribeFromNameChanges()
231 {
232     if (nameChangedToken_) {
233         Name()->OnChanged()->RemoveHandler(nameChangedToken_);
234         nameChangedToken_ = { 0 };
235     }
236 }
237 
IsResourceClassType()238 bool 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 /*
248 bool 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 */
Build(const IMetadata::Ptr & data)269 bool 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 
GetName() const292 BASE_NS::string NodeImpl::GetName() const
293 {
294     return META_NS::GetValue(Name());
295 }
296 
GetEcs() const297 CORE_NS::IEcs::Ptr NodeImpl::GetEcs() const
298 {
299     if (ecsObject_) {
300         return ecsObject_->GetEcs();
301     }
302     return CORE_NS::IEcs::Ptr {};
303 }
304 
SetEntity(CORE_NS::IEcs::Ptr ecs,CORE_NS::Entity entity)305 void NodeImpl::SetEntity(CORE_NS::IEcs::Ptr ecs, CORE_NS::Entity entity)
306 {
307     if (ecsObject_) {
308         return ecsObject_->SetEntity(ecs, entity);
309     }
310 }
311 
GetEntity() const312 CORE_NS::Entity NodeImpl::GetEntity() const
313 {
314     if (ecsObject_) {
315         return ecsObject_->GetEntity();
316     }
317     return CORE_NS::Entity {};
318 }
319 
BindObject(CORE_NS::IEcs::Ptr ecsInstance,CORE_NS::Entity entity)320 void NodeImpl::BindObject(CORE_NS::IEcs::Ptr ecsInstance, CORE_NS::Entity entity)
321 {
322     if (ecsObject_) {
323         ecsObject_->BindObject(ecsInstance, entity);
324     }
325 }
326 
DefineTargetProperties(BASE_NS::unordered_map<BASE_NS::string_view,BASE_NS::vector<BASE_NS::string_view>> names)327 void 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 
GetAttachments()335 BASE_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 
AddAttachment(CORE_NS::Entity entity)343 void NodeImpl::AddAttachment(CORE_NS::Entity entity)
344 {
345     if (ecsObject_) {
346         ecsObject_->AddAttachment(entity);
347     }
348 }
349 
RemoveAttachment(CORE_NS::Entity entity)350 void NodeImpl::RemoveAttachment(CORE_NS::Entity entity)
351 {
352     if (ecsObject_) {
353         ecsObject_->RemoveAttachment(entity);
354     }
355 }
356 
CloneEcs(const BASE_NS::string & name,META_NS::IObject::Ptr target) const357 void 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 
EcsScene() const401 SCENE_NS::IEcsScene::Ptr NodeImpl::EcsScene() const
402 {
403     return ecsScene_.lock();
404 }
405 
EcsObject() const406 SCENE_NS::IEcsObject::Ptr NodeImpl::EcsObject() const
407 {
408     return ecsObject_;
409 }
410 
SceneHolder() const411 SceneHolder::Ptr NodeImpl::SceneHolder() const
412 {
413     return sceneHolder_.lock();
414 }
415 
ClaimOwnershipOfEntity(bool ownsEntity)416 void 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 
RemoveIndex(size_t index)437 void NodeImpl::RemoveIndex(size_t index)
438 {
439     SCENE_PLUGIN_VERBOSE_LOG("remove node from %zu", index);
440     removeIndex_ = index;
441 }
442 
SetIndex(size_t index)443 void 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 
GetScene() const468 SCENE_NS::IScene::Ptr NodeImpl::GetScene() const
469 {
470     return interface_pointer_cast<SCENE_NS::IScene>(ecsScene_);
471 }
472 
RenameEntity(const BASE_NS::string & name)473 void 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
RecursivelyEnableCache(bool isActive)493 void 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
UpdateChildrenPath()509 void 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 
ShouldExport() const529 bool 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 
Initialize(SCENE_NS::IEcsScene::Ptr & scene,SCENE_NS::IEcsObject::Ptr & ecsObject,SCENE_NS::INode::Ptr parent,const BASE_NS::string & path,const BASE_NS::string & name,SceneHolder::WeakPtr sceneHolder,CORE_NS::Entity entity)582 bool 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 
BindObject(SCENE_NS::INode::Ptr node)611 void 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 
CompleteInitialization(const BASE_NS::string & path)633 bool 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 
SetPathWithEcsNode(const BASE_NS::shared_ptr<NodeImpl> & self,const BASE_NS::string & name,SceneHolder::Ptr sceneHolder,const CORE3D_NS::ISceneNode * ecsNode)738 void 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 
SetPathWithoutNode(const BASE_NS::shared_ptr<NodeImpl> & self,const BASE_NS::string & name,const BASE_NS::string & fullPath,SceneHolder::Ptr sceneHolder)786 bool 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.
SetPath(const BASE_NS::string & path,const BASE_NS::string & name,CORE_NS::Entity entity)840 void 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 
HasLifecycleInfo()881 bool 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 
GetLifecycleInfo(bool create)905 META_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 
EnsureEcsBinding(SCENE_NS::IEcsScene::Ptr scene,bool force)923 void 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 
ListBoundProperties() const944 BASE_NS::vector<SCENE_NS::IProxyObject::PropertyPair> NodeImpl::ListBoundProperties() const
945 {
946     return propHandler_.GetBoundProperties();
947 }
948 
BuildChildrenIterateOver(const BASE_NS::shared_ptr<NodeImpl> & self,const SCENE_NS::IEcsScene::Ptr & ecs,const BASE_NS::string & fullPath,BASE_NS::array_view<CORE3D_NS::ISceneNode * const> children)949 void 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 
BuildChildren(SCENE_NS::INode::BuildBehavior options)986 bool 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 
CheckChildrenStatus()1064 void 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 
GetGlobalTransform() const1092 BASE_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 
SetGlobalTransform(const BASE_NS::Math::Mat4X4 & mat)1110 void 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 
MonitorChild(SCENE_NS::INode::Ptr node)1140 void 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 
SetMeshToEngine()1152 void 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 
SetMesh(SCENE_NS::IMesh::Ptr mesh)1172 void 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 
CreateMeshProxy(size_t count,SCENE_NS::IMesh::Ptr mesh)1181 SCENE_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 
SetMultiMeshProxy(SCENE_NS::IMultiMeshProxy::Ptr multimesh)1198 void NodeImpl::SetMultiMeshProxy(SCENE_NS::IMultiMeshProxy::Ptr multimesh)
1199 {
1200     multimesh_ = multimesh;
1201 }
1202 
GetMultiMeshProxy() const1203 SCENE_NS::IMultiMeshProxy::Ptr NodeImpl::GetMultiMeshProxy() const
1204 {
1205     return multimesh_;
1206 }
1207 
ReleaseEntityOwnership()1208 void 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 
Destroy()1238 void 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 
GetWorldMatrixComponentAABB(bool isRecursive) const1256 SCENE_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 
GetTransformComponentAABB(bool isRecursive) const1289 SCENE_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 
SetStatus(SCENE_NS::INode::NodeStatus status)1322 void NodeImpl::SetStatus(SCENE_NS::INode::NodeStatus status)
1323 {
1324     META_ACCESS_PROPERTY(Status)->SetValue(status);
1325 }
1326 /*
1327 bool 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 */
SetSuperInstance(const IObject::Ptr & aggr,const IObject::Ptr & super)1338 void 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 
SetParent(const IObject::Ptr & parent)1345 void NodeImpl::SetParent(const IObject::Ptr& parent)
1346 {
1347     if (mutableContainable_) {
1348         mutableContainable_->SetParent(parent);
1349     } else {
1350         parent_ = parent;
1351     }
1352 }
1353 
GetParent() const1354 META_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 
GetMesh() const1368 SCENE_NS::IMesh::Ptr NodeImpl::GetMesh() const
1369 {
1370     return META_NS::GetValue(Mesh());
1371 }
1372 
InitializeMesh(const SCENE_NS::IMesh::Ptr & mesh,const BASE_NS::shared_ptr<NodeImpl> & node,const BASE_NS::string_view entityName)1373 void 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 
InitializeMesh(const SCENE_NS::IMesh::Ptr & mesh,const BASE_NS::shared_ptr<SCENE_NS::IEcsObject> & self)1384 void 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 
GetMeshFromEngine()1405 SCENE_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 
1441 using PropertyNameCriteria = BASE_NS::unordered_map<BASE_NS::string_view, BASE_NS::vector<BASE_NS::string_view>>;
PropertyNameMask()1442 PropertyNameCriteria& NodeImpl::PropertyNameMask()
1443 {
1444     return ecs_property_names_;
1445 }
1446 
Resolve(const META_NS::RefUri & uri) const1447 META_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 
SCENE_BEGIN_NAMESPACE()1470 SCENE_BEGIN_NAMESPACE()
1471 void RegisterNodeImpl()
1472 {
1473     META_NS::GetObjectRegistry().RegisterObjectType<NodeImpl>();
1474 }
UnregisterNodeImpl()1475 void UnregisterNodeImpl()
1476 {
1477     META_NS::GetObjectRegistry().UnregisterObjectType<NodeImpl>();
1478 }
1479 SCENE_END_NAMESPACE()
1480