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