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