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
16 #include "internal_scene.h"
17
18 #include <chrono>
19 #include <inttypes.h>
20 #include <mutex>
21 #include <scene/ext/intf_converting_value.h>
22 #include <scene/ext/intf_create_entity.h>
23 #include <scene/ext/util.h>
24 #include <scene/interface/intf_light.h>
25 #include <scene/interface/intf_mesh.h>
26 #include <scene/interface/intf_scene.h>
27 #include <scene/interface/intf_text.h>
28
29 #include <3d/implementation_uids.h>
30 #include <render/intf_render_context.h>
31 #include <render/intf_renderer.h>
32
33 #include <meta/api/engine/util.h>
34 #include <meta/interface/intf_startable.h>
35
36 #include "component/generic_component.h"
37 #include "node/startable_handler.h"
38 #include "resource/ecs_animation.h"
39 #include "ecs_object.h"
40
SCENE_BEGIN_NAMESPACE()41 SCENE_BEGIN_NAMESPACE()
42
43 InternalScene::InternalScene(const IScene::Ptr& scene, IRenderContext::Ptr context, SceneOptions opts)
44 : scene_(scene), options_(BASE_NS::move(opts))
45 {
46 if (auto getter = interface_cast<IApplicationContextProvider>(context)) {
47 context_ = getter->GetApplicationContext();
48 }
49
50 graphicsContext3D_ = CORE_NS::CreateInstance<CORE3D_NS::IGraphicsContext>(
51 *context->GetRenderer()->GetInterface<CORE_NS::IClassFactory>(), CORE3D_NS::UID_GRAPHICS_CONTEXT);
52 graphicsContext3D_->Init();
53 }
54
~InternalScene()55 InternalScene::~InternalScene() {}
56
Initialize()57 bool InternalScene::Initialize()
58 {
59 ecs_.reset(new Ecs);
60 if (!ecs_->Initialize(self_.lock(), options_)) {
61 CORE_LOG_E("failed to initialize ecs");
62 return false;
63 }
64 return true;
65 }
66
GetOptions() const67 SceneOptions InternalScene::GetOptions() const
68 {
69 return options_;
70 }
71
Uninitialize()72 void InternalScene::Uninitialize()
73 {
74 CORE_LOG_D("InternalScene::Uninitialize");
75 {
76 std::unique_lock lock { mutex_ };
77 syncs_.clear();
78 }
79 nodes_.clear();
80 animations_.clear();
81 componentFactories_.clear();
82 renderingCameras_.clear();
83
84 if (ecs_) {
85 ecs_->Uninitialize();
86 }
87
88 if (const auto rctx = GetRenderContextPtr()) {
89 // Do a "empty render" to flush out the gpu resources instantly.
90 rctx->GetRenderer().RenderFrame({});
91 }
92 }
93
CreateDefaultEntity(CORE_NS::IEcs & ecs)94 static CORE_NS::Entity CreateDefaultEntity(CORE_NS::IEcs& ecs)
95 {
96 CORE_NS::IEntityManager& em = ecs.GetEntityManager();
97 return em.Create();
98 }
99
CreateNode(BASE_NS::string_view path,META_NS::ObjectId id)100 INode::Ptr InternalScene::CreateNode(BASE_NS::string_view path, META_NS::ObjectId id)
101 {
102 auto& r = META_NS::GetObjectRegistry();
103
104 if (ecs_->FindNode(path)) {
105 CORE_LOG_E("Node exists already [path=%s]", BASE_NS::string(path).c_str());
106 return nullptr;
107 }
108
109 auto parent = ecs_->FindNodeParent(path);
110 if (!parent) {
111 CORE_LOG_E("No parent for node [path=%s]", BASE_NS::string(path).c_str());
112 return nullptr;
113 }
114
115 if (!id.IsValid()) {
116 id = ClassId::Node;
117 }
118
119 auto node = r.Create<INode>(id);
120 if (!node) {
121 CORE_LOG_E(
122 "Failed to create node object [path=%s, id=%s]", BASE_NS::string(path).c_str(), id.ToString().c_str());
123 return nullptr;
124 }
125 CORE_NS::Entity ent;
126 if (auto ce = interface_pointer_cast<ICreateEntity>(node)) {
127 ent = ce->CreateEntity(self_.lock());
128 } else {
129 ent = CreateDefaultEntity(*ecs_->ecs);
130 }
131 if (!CORE_NS::EntityUtil::IsValid(ent)) {
132 CORE_LOG_E("Failed to create entity [path=%s, id=%s]", BASE_NS::string(path).c_str(), id.ToString().c_str());
133 return nullptr;
134 }
135 ecs_->AddDefaultComponents(ent);
136 ecs_->SetNodeName(ent, EntityName(path)); // remove when reworking names for scene objects
137 if (!ConstructNodeImpl(ent, node)) {
138 ecs_->RemoveEntity(ent);
139 return nullptr;
140 ;
141 }
142 ecs_->SetNodeParentAndName(ent, EntityName(path), parent);
143 return node;
144 }
145
CreateObject(META_NS::ObjectId id)146 META_NS::IObject::Ptr InternalScene::CreateObject(META_NS::ObjectId id)
147 {
148 auto& r = META_NS::GetObjectRegistry();
149 auto md = CreateRenderContextArg(GetContext());
150 if (md) {
151 md->AddProperty(META_NS::ConstructProperty<IInternalScene::Ptr>("Scene", self_.lock()));
152 }
153 auto object = r.Create<META_NS::IObject>(id, md);
154 if (!object) {
155 CORE_LOG_E("Failed to create scene object [id=%s]", id.ToString().c_str());
156 return nullptr;
157 }
158 if (auto acc = interface_cast<IEcsObjectAccess>(object)) {
159 CORE_NS::Entity ent;
160 if (auto ce = interface_pointer_cast<ICreateEntity>(object)) {
161 ent = ce->CreateEntity(self_.lock());
162 } else {
163 ent = CreateDefaultEntity(*ecs_->ecs);
164 }
165 if (!CORE_NS::EntityUtil::IsValid(ent)) {
166 CORE_LOG_E("Failed to create entity [id=%s]", id.ToString().c_str());
167 return nullptr;
168 }
169 auto eobj = ecs_->GetEcsObject(ent);
170 if (!acc->SetEcsObject(eobj)) {
171 return nullptr;
172 }
173 }
174 return object;
175 }
176
ConstructNodeImpl(CORE_NS::Entity ent,INode::Ptr node) const177 INode::Ptr InternalScene::ConstructNodeImpl(CORE_NS::Entity ent, INode::Ptr node) const
178 {
179 auto acc = interface_cast<IEcsObjectAccess>(node);
180 if (!acc) {
181 return nullptr;
182 }
183 auto& r = META_NS::GetObjectRegistry();
184
185 auto eobj = ecs_->GetEcsObject(ent);
186 if (!eobj) {
187 return nullptr;
188 }
189 AttachComponents(node, eobj, ent);
190
191 if (!acc->SetEcsObject(eobj)) {
192 return nullptr;
193 }
194
195 nodes_[ent] = node;
196 return node;
197 }
198
DeducePrimaryNodeType(CORE_NS::Entity ent) const199 META_NS::ObjectId InternalScene::DeducePrimaryNodeType(CORE_NS::Entity ent) const
200 {
201 if (ecs_->cameraComponentManager->HasComponent(ent)) {
202 return ClassId::CameraNode;
203 }
204 if (ecs_->lightComponentManager->HasComponent(ent)) {
205 return ClassId::LightNode;
206 }
207 if (ecs_->textComponentManager && ecs_->textComponentManager->HasComponent(ent)) {
208 return ClassId::TextNode;
209 }
210 if (ecs_->renderMeshComponentManager->HasComponent(ent)) {
211 return ClassId::MeshNode;
212 }
213 return ClassId::Node;
214 }
215
ConstructNode(CORE_NS::Entity ent,META_NS::ObjectId id) const216 INode::Ptr InternalScene::ConstructNode(CORE_NS::Entity ent, META_NS::ObjectId id) const
217 {
218 auto& r = META_NS::GetObjectRegistry();
219 if (!id.IsValid()) {
220 id = DeducePrimaryNodeType(ent);
221 }
222
223 auto node = r.Create<INode>(id);
224 if (!node) {
225 CORE_LOG_E("Failed to create node object [id=%s]", id.ToString().c_str());
226 return nullptr;
227 }
228
229 return ConstructNodeImpl(ent, node);
230 }
231
CreateEcsComponent(const INode::Ptr & node,BASE_NS::string_view componentName)232 IComponent::Ptr InternalScene::CreateEcsComponent(const INode::Ptr& node, BASE_NS::string_view componentName)
233 {
234 // First check we already have the component
235 auto attach = interface_cast<META_NS::IAttach>(node);
236 if (!attach) {
237 return {};
238 }
239 if (auto cont = attach->GetAttachmentContainer(true)) {
240 if (auto existing = cont->FindAny<IComponent>(componentName, META_NS::TraversalType::NO_HIERARCHY)) {
241 return existing;
242 }
243 }
244 // Then find a component manager with a matching name
245 IEcsObject::Ptr ecso;
246 if (auto acc = interface_cast<IEcsObjectAccess>(node)) {
247 ecso = acc->GetEcsObject();
248 }
249 if (ecs_ && ecso) {
250 if (auto ecs = ecs_->GetNativeEcs()) {
251 for (auto&& manager : ecs->GetComponentManagers()) {
252 if (manager->GetName() == componentName) {
253 // Also create the Ecs component if not there already
254 if (auto component = CreateComponent(manager, ecso, true)) {
255 // We don't call component->PopulateAllProperties() here, i.e. if the component was newly
256 // created its properties will not be populated
257 attach->Attach(component);
258 return component;
259 }
260 }
261 }
262 }
263 }
264 return {};
265 }
266
CreateComponent(CORE_NS::IComponentManager * m,const IEcsObject::Ptr & ecsObject,bool createEcsComponent) const267 IComponent::Ptr InternalScene::CreateComponent(
268 CORE_NS::IComponentManager* m, const IEcsObject::Ptr& ecsObject, bool createEcsComponent) const
269 {
270 if (!m) {
271 return {};
272 }
273 auto& r = META_NS::GetObjectRegistry();
274 IComponent::Ptr comp;
275 ;
276 if (createEcsComponent) {
277 if (auto entity = ecsObject->GetEntity(); CORE_NS::EntityUtil::IsValid(entity)) {
278 if (!m->HasComponent(entity)) {
279 m->Create(entity);
280 }
281 }
282 }
283 if (auto fac = FindComponentFactory(m->GetUid())) {
284 comp = fac->CreateComponent(ecsObject);
285 } else {
286 auto md = r.Create<META_NS::IMetadata>(META_NS::ClassId::Object);
287 md->AddProperty(META_NS::ConstructProperty<BASE_NS::string>("Component", BASE_NS::string(m->GetName())));
288 comp = r.Create<IComponent>(ClassId::GenericComponent, md);
289 if (auto acc = interface_cast<IEcsObjectAccess>(comp)) {
290 if (!acc->SetEcsObject(ecsObject)) {
291 return nullptr;
292 }
293 }
294 }
295 return comp;
296 }
297
AttachComponents(const INode::Ptr & node,const IEcsObject::Ptr & ecsObject,CORE_NS::Entity ent) const298 void InternalScene::AttachComponents(
299 const INode::Ptr& node, const IEcsObject::Ptr& ecsObject, CORE_NS::Entity ent) const
300 {
301 auto& r = META_NS::GetObjectRegistry();
302 auto att = interface_cast<META_NS::IAttach>(node);
303 if (!att) {
304 return;
305 }
306
307 auto attachments = att->GetAttachmentContainer(true);
308
309 BASE_NS::vector<CORE_NS::IComponentManager*> managers;
310 ecs_->ecs->GetComponents(ent, managers);
311 for (auto m : managers) {
312 if (!attachments->FindByName(m->GetName())) {
313 if (auto comp = CreateComponent(m, ecsObject, false)) {
314 att->Attach(comp);
315 } else {
316 CORE_LOG_E("Failed to construct component for '%s'", BASE_NS::string(m->GetName()).c_str());
317 }
318 }
319 }
320 }
321
FindNode(CORE_NS::Entity ent,META_NS::ObjectId id) const322 INode::Ptr InternalScene::FindNode(CORE_NS::Entity ent, META_NS::ObjectId id) const
323 {
324 if (ecs_->IsNodeEntity(ent)) {
325 auto it = nodes_.find(ent);
326 if (it != nodes_.end()) {
327 return it->second;
328 }
329 return ConstructNode(ent, id);
330 }
331
332 CORE_LOG_W("Could not find entity: %" PRIu64, ent.id);
333 return nullptr;
334 }
335
FindNode(BASE_NS::string_view path,META_NS::ObjectId id) const336 INode::Ptr InternalScene::FindNode(BASE_NS::string_view path, META_NS::ObjectId id) const
337 {
338 auto npath = NormalisePath(path);
339 auto n = ecs_->FindNode(npath);
340 if (n) {
341 auto it = nodes_.find(n->GetEntity());
342 if (it != nodes_.end()) {
343 return it->second;
344 }
345 return ConstructNode(n->GetEntity(), id);
346 }
347
348 CORE_LOG_W("Could not find node: %s", BASE_NS::string(npath).c_str());
349 return nullptr;
350 }
351
ReleaseCached(NodesType::iterator it)352 INode::Ptr InternalScene::ReleaseCached(NodesType::iterator it)
353 {
354 auto node = BASE_NS::move(it->second);
355 nodes_.erase(it);
356 if (auto i = interface_cast<INodeNotify>(node)) {
357 if (i->IsListening()) {
358 ListenNodeChanges(false);
359 }
360 }
361 return node;
362 }
363
ReleaseChildNodes(const IEcsObject::Ptr & eobj)364 void InternalScene::ReleaseChildNodes(const IEcsObject::Ptr& eobj)
365 {
366 if (auto n = ecs_->GetNode(eobj->GetEntity())) {
367 for (auto&& c : n->GetChildren()) {
368 auto it = nodes_.find(c->GetEntity());
369 if (it != nodes_.end()) {
370 auto nn = it->second;
371 ReleaseNode(BASE_NS::move(nn), true);
372 }
373 }
374 }
375 }
376
ReleaseNode(INode::Ptr && node,bool recursive)377 bool InternalScene::ReleaseNode(INode::Ptr&& node, bool recursive)
378 {
379 if (node) {
380 IEcsObject::Ptr eobj;
381 if (auto acc = interface_cast<IEcsObjectAccess>(node)) {
382 eobj = acc->GetEcsObject();
383 }
384 node.reset();
385 if (eobj) {
386 auto it = nodes_.find(eobj->GetEntity());
387 if (it != nodes_.end()) {
388 // are we the only owner?
389 if (it->second.use_count() == 1) {
390 node = ReleaseCached(it);
391 }
392 }
393 if (recursive) {
394 ReleaseChildNodes(eobj);
395 }
396 if (node) {
397 ecs_->RemoveEcsObject(eobj);
398 return true;
399 }
400 }
401 }
402 return false;
403 }
404
RemoveNode(const INode::Ptr & node)405 bool InternalScene::RemoveNode(const INode::Ptr& node)
406 {
407 if (node) {
408 if (auto acc = interface_cast<IEcsObjectAccess>(node)) {
409 if (auto eobj = acc->GetEcsObject()) {
410 auto decents = ecs_->GetNodeDescendants(eobj->GetEntity());
411 for (auto&& ent : decents) {
412 if (auto it = nodes_.find(ent); it != nodes_.end()) {
413 ReleaseCached(it);
414 }
415 ecs_->RemoveEntity(ent);
416 }
417 return true;
418 }
419 }
420 }
421 return false;
422 }
423
424 /// Returns a list of instantiated child nodes of root (including root) which implement INodeNotify
GetNotifiableNodesFromHierarchy(CORE_NS::Entity root)425 BASE_NS::vector<INodeNotify::Ptr> InternalScene::GetNotifiableNodesFromHierarchy(CORE_NS::Entity root)
426 {
427 BASE_NS::vector<INodeNotify::Ptr> notify;
428 auto findNode = [this](CORE_NS::Entity entity) {
429 auto n = nodes_.find(entity);
430 return n != nodes_.end() ? interface_pointer_cast<INodeNotify>(n->second) : nullptr;
431 };
432 // Add root to the list
433 if (auto n = findNode(root)) {
434 notify.emplace_back(BASE_NS::move(n));
435 }
436 // Add descendants to the list
437 auto descendants = ecs_->GetNodeDescendants(root);
438 notify.reserve(descendants.size() + 1);
439 for (auto&& d : descendants) {
440 if (auto n = findNode(d)) {
441 notify.emplace_back(BASE_NS::move(n));
442 }
443 }
444 return notify;
445 }
446
SetEntityActive(const BASE_NS::shared_ptr<IEcsObject> & child,bool active)447 void InternalScene::SetEntityActive(const BASE_NS::shared_ptr<IEcsObject>& child, bool active)
448 {
449 if (!child || !ecs_) {
450 return;
451 }
452 const auto entity = child->GetEntity();
453 if (!active) {
454 for (auto&& node : GetNotifiableNodesFromHierarchy(entity)) {
455 node->OnNodeActiveStateChanged(INodeNotify::NodeActiteStateInfo::DEACTIVATING);
456 }
457 }
458 ecs_->SetNodesActive(entity, active);
459 if (active) {
460 for (auto&& node : GetNotifiableNodesFromHierarchy(entity)) {
461 node->OnNodeActiveStateChanged(INodeNotify::NodeActiteStateInfo::ACTIVATED);
462 }
463 }
464 }
465
GetChildren(const IEcsObject::Ptr & obj) const466 BASE_NS::vector<INode::Ptr> InternalScene::GetChildren(const IEcsObject::Ptr& obj) const
467 {
468 BASE_NS::vector<INode::Ptr> res;
469 if (auto node = ecs_->GetNode(obj->GetEntity())) {
470 for (auto&& c : node->GetChildren()) {
471 if (auto n = FindNode(c->GetEntity(), {})) {
472 res.push_back(n);
473 }
474 }
475 }
476 return res;
477 }
RemoveChild(const BASE_NS::shared_ptr<IEcsObject> & object,const BASE_NS::shared_ptr<IEcsObject> & child)478 bool InternalScene::RemoveChild(
479 const BASE_NS::shared_ptr<IEcsObject>& object, const BASE_NS::shared_ptr<IEcsObject>& child)
480 {
481 bool ret = false;
482 if (auto node = ecs_->GetNode(object->GetEntity())) {
483 if (auto childNode = ecs_->GetNode(child->GetEntity())) {
484 ret = node->RemoveChild(*childNode);
485 if (ret) {
486 SetEntityActive(child, false);
487 }
488 }
489 }
490 return ret;
491 }
AddChild(const BASE_NS::shared_ptr<IEcsObject> & object,const INode::Ptr & child,size_t index)492 bool InternalScene::AddChild(const BASE_NS::shared_ptr<IEcsObject>& object, const INode::Ptr& child, size_t index)
493 {
494 bool ret = false;
495 if (ecs_->IsNodeEntity(object->GetEntity())) {
496 if (auto acc = interface_cast<IEcsObjectAccess>(child)) {
497 auto ecsobj = acc->GetEcsObject();
498 SetEntityActive(ecsobj, true);
499 if (auto node = ecs_->GetNode(object->GetEntity())) {
500 if (auto childNode = ecs_->GetNode(ecsobj->GetEntity())) {
501 if (node->InsertChild(index, *childNode)) {
502 nodes_[ecsobj->GetEntity()] = child;
503 ret = true;
504 }
505 }
506 }
507 }
508 }
509 return ret;
510 }
511
GetScene() const512 BASE_NS::shared_ptr<IScene> InternalScene::GetScene() const
513 {
514 return scene_.lock();
515 }
516
SchedulePropertyUpdate(const IEcsObject::Ptr & obj)517 void InternalScene::SchedulePropertyUpdate(const IEcsObject::Ptr& obj)
518 {
519 std::unique_lock lock { mutex_ };
520 syncs_[obj.get()] = obj;
521 }
522
SyncProperties()523 void InternalScene::SyncProperties()
524 {
525 UpdateSyncProperties(false);
526 }
527
UpdateSyncProperties(bool resetPending)528 bool InternalScene::UpdateSyncProperties(bool resetPending)
529 {
530 bool pending = false;
531 BASE_NS::unordered_map<void*, IEcsObject::WeakPtr> syncs;
532 {
533 std::unique_lock lock { mutex_ };
534 syncs = BASE_NS::move(syncs_);
535 pending = pendingRender_;
536 if (resetPending) {
537 pendingRender_ = false;
538 }
539 }
540
541 for (auto&& v : syncs) {
542 if (auto o = v.second.lock()) {
543 o->SyncProperties();
544 }
545 }
546 return pending;
547 }
548
Update(const UpdateInfo & info)549 void InternalScene::Update(const UpdateInfo& info)
550 {
551 using namespace std::chrono;
552 bool pending;
553 if (info.syncProperties) {
554 pending = UpdateSyncProperties(true);
555 } else {
556 std::unique_lock lock { mutex_ };
557 pending = pendingRender_;
558 pendingRender_ = false;
559 }
560
561 ecs_->ecs->ProcessEvents();
562
563 uint64_t currentTime {};
564
565 if (info.clock) {
566 currentTime = info.clock->GetTime().ToMicroseconds();
567 } else {
568 currentTime =
569 static_cast<uint64_t>(duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count());
570 }
571
572 if (firstTime_ == ~0u) {
573 previousFrameTime_ = firstTime_ = currentTime;
574 }
575 auto deltaTime = currentTime - previousFrameTime_;
576 constexpr auto limitHz = duration_cast<microseconds>(duration<float, std::ratio<1, 15u>>(1)).count();
577 if (deltaTime > limitHz) {
578 deltaTime = limitHz; // clamp the time step to no longer than 15hz.
579 }
580 previousFrameTime_ = currentTime;
581 const uint64_t totalTime = currentTime - firstTime_;
582
583 bool needsRender = ecs_->ecs->Update(totalTime, deltaTime);
584
585 ecs_->ecs->ProcessEvents();
586
587 if ((needsRender && mode_ != RenderMode::MANUAL) || pending) {
588 auto renderHandles = graphicsContext3D_->GetRenderNodeGraphs(*ecs_->ecs);
589 if (!renderHandles.empty()) {
590 // The scene needs to be rendered.
591 if (auto rctx = GetRenderContextPtr()) {
592 RENDER_NS::IRenderer& renderer = rctx->GetRenderer();
593 renderer.RenderDeferred(renderHandles);
594 NotifyRenderingCameras();
595 }
596 }
597 }
598 }
599
Update(bool syncProperties)600 void InternalScene::Update(bool syncProperties)
601 {
602 IInternalSceneCore::UpdateInfo info;
603 info.syncProperties = syncProperties;
604 Update(info);
605 }
606
NotifyRenderingCameras()607 void InternalScene::NotifyRenderingCameras()
608 {
609 for (auto it = renderingCameras_.begin(); it != renderingCameras_.end();) {
610 if (auto c = it->lock()) {
611 c->NotifyRenderTargetChanged();
612 ++it;
613 } else {
614 it = renderingCameras_.erase(it);
615 }
616 }
617 }
618
GetCameras() const619 BASE_NS::vector<ICamera::Ptr> InternalScene::GetCameras() const
620 {
621 BASE_NS::vector<ICamera::Ptr> ret;
622
623 for (size_t i = 0; i != ecs_->cameraComponentManager->GetComponentCount(); ++i) {
624 if (auto n = interface_pointer_cast<ICamera>(
625 FindNode(ecs_->cameraComponentManager->GetEntity(i), ClassId::CameraNode))) {
626 ret.push_back(n);
627 }
628 }
629
630 return ret;
631 }
632
GetAnimations() const633 BASE_NS::vector<META_NS::IAnimation::Ptr> InternalScene::GetAnimations() const
634 {
635 BASE_NS::vector<META_NS::IAnimation::Ptr> ret;
636
637 for (size_t i = 0; i != ecs_->animationComponentManager->GetComponentCount(); ++i) {
638 auto ent = ecs_->animationComponentManager->GetEntity(i);
639 META_NS::IAnimation::Ptr anim;
640 if (auto it = animations_.find(ent); it != animations_.end()) {
641 anim = it->second.lock();
642 }
643 if (!anim) {
644 anim = META_NS::GetObjectRegistry().Create<META_NS::IAnimation>(ClassId::EcsAnimation);
645 if (auto acc = interface_cast<IEcsObjectAccess>(anim)) {
646 acc->SetEcsObject(ecs_->GetEcsObject(ent));
647 animations_[ent] = anim;
648 }
649 }
650 if (anim) {
651 ret.push_back(anim);
652 } else {
653 CORE_LOG_W("Failed to create EcsAnimation object");
654 }
655 }
656
657 return ret;
658 }
659
RegisterComponent(const BASE_NS::Uid & id,const IComponentFactory::Ptr & p)660 void InternalScene::RegisterComponent(const BASE_NS::Uid& id, const IComponentFactory::Ptr& p)
661 {
662 componentFactories_[id] = p;
663 }
664
UnregisterComponent(const BASE_NS::Uid & id)665 void InternalScene::UnregisterComponent(const BASE_NS::Uid& id)
666 {
667 componentFactories_.erase(id);
668 }
669
FindComponentFactory(const BASE_NS::Uid & id) const670 BASE_NS::shared_ptr<IComponentFactory> InternalScene::FindComponentFactory(const BASE_NS::Uid& id) const
671 {
672 auto it = componentFactories_.find(id);
673 return it != componentFactories_.end() ? it->second : nullptr;
674 }
675
SyncProperty(const META_NS::IProperty::ConstPtr & p,META_NS::EngineSyncDirection dir)676 bool InternalScene::SyncProperty(const META_NS::IProperty::ConstPtr& p, META_NS::EngineSyncDirection dir)
677 {
678 META_NS::IEngineValue::Ptr value = GetEngineValueFromProperty(p);
679 if (!value) {
680 if (auto i = META_NS::GetFirstValueFromProperty<IConvertingValue>(p)) {
681 value = GetEngineValueFromProperty(i->GetTargetProperty());
682 }
683 }
684 META_NS::InterfaceUniqueLock valueLock { value };
685 return value && value->Sync(dir);
686 }
687
AddRenderingCamera(const IInternalCamera::Ptr & camera)688 void InternalScene::AddRenderingCamera(const IInternalCamera::Ptr& camera)
689 {
690 for (auto&& v : renderingCameras_) {
691 if (camera == v.lock()) {
692 return;
693 }
694 }
695 renderingCameras_.push_back(camera);
696 }
RemoveRenderingCamera(const IInternalCamera::Ptr & camera)697 void InternalScene::RemoveRenderingCamera(const IInternalCamera::Ptr& camera)
698 {
699 for (auto it = renderingCameras_.begin(); it != renderingCameras_.end(); ++it) {
700 if (camera == it->lock()) {
701 renderingCameras_.erase(it);
702 return;
703 }
704 }
705 }
706
SetRenderMode(RenderMode mode)707 bool InternalScene::SetRenderMode(RenderMode mode)
708 {
709 mode_ = mode;
710 ecs_->ecs->SetRenderMode(
711 mode == RenderMode::IF_DIRTY ? CORE_NS::IEcs::RENDER_IF_DIRTY : CORE_NS::IEcs::RENDER_ALWAYS);
712 return true;
713 }
GetRenderMode() const714 RenderMode InternalScene::GetRenderMode() const
715 {
716 return mode_;
717 }
RenderFrame()718 void InternalScene::RenderFrame()
719 {
720 std::unique_lock lock { mutex_ };
721 pendingRender_ = true;
722 }
HasPendingRender() const723 bool InternalScene::HasPendingRender() const
724 {
725 std::unique_lock lock { mutex_ };
726 return pendingRender_;
727 }
728
MapHitResults(const BASE_NS::vector<CORE3D_NS::RayCastResult> & res,const RayCastOptions & options) const729 NodeHits InternalScene::MapHitResults(
730 const BASE_NS::vector<CORE3D_NS::RayCastResult>& res, const RayCastOptions& options) const
731 {
732 NodeHits result;
733 CORE3D_NS::ISceneNode* n = nullptr;
734 if (auto obj = interface_cast<IEcsObjectAccess>(options.node)) {
735 if (auto ecs = obj->GetEcsObject()) {
736 n = ecs_->GetNode(ecs->GetEntity());
737 }
738 }
739
740 for (auto&& v : res) {
741 NodeHit h;
742 if (v.node && (!n || n->IsAncestorOf(*v.node))) {
743 h.node = FindNode(v.node->GetEntity(), {});
744 h.distance = v.distance;
745 h.distanceToCenter = v.centerDistance;
746 h.position = v.worldPosition;
747 result.push_back(BASE_NS::move(h));
748 }
749 }
750 return result;
751 }
752
CastRay(const BASE_NS::Math::Vec3 & pos,const BASE_NS::Math::Vec3 & dir,const RayCastOptions & options) const753 NodeHits InternalScene::CastRay(
754 const BASE_NS::Math::Vec3& pos, const BASE_NS::Math::Vec3& dir, const RayCastOptions& options) const
755 {
756 NodeHits result;
757 if (ecs_->picking) {
758 result = MapHitResults(ecs_->picking->RayCast(*ecs_->ecs, pos, dir, options.layerMask), options);
759 }
760 return result;
761 }
CastRay(const IEcsObject::ConstPtr & entity,const BASE_NS::Math::Vec2 & pos,const RayCastOptions & options) const762 NodeHits InternalScene::CastRay(
763 const IEcsObject::ConstPtr& entity, const BASE_NS::Math::Vec2& pos, const RayCastOptions& options) const
764 {
765 NodeHits result;
766 if (ecs_->picking) {
767 result = MapHitResults(
768 ecs_->picking->RayCastFromCamera(*ecs_->ecs, entity->GetEntity(), pos, options.layerMask), options);
769 }
770 return result;
771 }
ScreenPositionToWorld(const IEcsObject::ConstPtr & entity,const BASE_NS::Math::Vec3 & pos) const772 BASE_NS::Math::Vec3 InternalScene::ScreenPositionToWorld(
773 const IEcsObject::ConstPtr& entity, const BASE_NS::Math::Vec3& pos) const
774 {
775 BASE_NS::Math::Vec3 result;
776 if (ecs_->picking) {
777 result = ecs_->picking->ScreenToWorld(*ecs_->ecs, entity->GetEntity(), pos);
778 }
779 return result;
780 }
WorldPositionToScreen(const IEcsObject::ConstPtr & entity,const BASE_NS::Math::Vec3 & pos) const781 BASE_NS::Math::Vec3 InternalScene::WorldPositionToScreen(
782 const IEcsObject::ConstPtr& entity, const BASE_NS::Math::Vec3& pos) const
783 {
784 BASE_NS::Math::Vec3 result;
785 if (ecs_->picking) {
786 result = ecs_->picking->WorldToScreen(*ecs_->ecs, entity->GetEntity(), pos);
787 }
788 return result;
789 }
790
ListenNodeChanges(bool enabled)791 void InternalScene::ListenNodeChanges(bool enabled)
792 {
793 bool change = false;
794 if (enabled) {
795 change = !nodeListening_++;
796 } else if (nodeListening_ > 0) {
797 change = !--nodeListening_;
798 }
799 if (change) {
800 ecs_->ListenNodeChanges(enabled);
801 }
802 }
803
OnChildChanged(META_NS::ContainerChangeType type,CORE_NS::Entity parent,CORE_NS::Entity childEntity,size_t index)804 void InternalScene::OnChildChanged(
805 META_NS::ContainerChangeType type, CORE_NS::Entity parent, CORE_NS::Entity childEntity, size_t index)
806 {
807 if (auto it = nodes_.find(parent); it != nodes_.end()) {
808 if (auto i = interface_cast<INodeNotify>(it->second)) {
809 if (auto child = FindNode(childEntity, {})) {
810 i->OnChildChanged(type, child, index);
811 } else {
812 CORE_LOG_W("child changed but cannot construct it?!");
813 }
814 }
815 }
816 }
817
GetNodes() const818 BASE_NS::vector<INode::Ptr> InternalScene::GetNodes() const
819 {
820 // This could be improved to traverse the Node system, and get a list of nodes that we have a wrapper for in a given
821 // traversal order
822 BASE_NS::vector<INode::Ptr> nodes;
823 nodes.reserve(nodes_.size());
824 for (auto&& n : nodes_) {
825 nodes.push_back(n.second);
826 }
827 return nodes;
828 }
829
StartAllStartables(META_NS::IStartableController::ControlBehavior behavior)830 void InternalScene::StartAllStartables(META_NS::IStartableController::ControlBehavior behavior)
831 {
832 if (options_.enableStartables) {
833 if (auto me = self_.lock()) {
834 for (auto&& n : GetNodes()) {
835 Internal::StartAllStartables(
836 me, StartableHandler::StartType::DEFERRED, interface_pointer_cast<META_NS::IObject>(n));
837 }
838 }
839 }
840 }
841
StopAllStartables(META_NS::IStartableController::ControlBehavior behavior)842 void InternalScene::StopAllStartables(META_NS::IStartableController::ControlBehavior behavior)
843 {
844 if (options_.enableStartables) {
845 for (auto&& n : GetNodes()) {
846 Internal::StopAllStartables(interface_pointer_cast<META_NS::IObject>(n));
847 }
848 }
849 }
850
851 SCENE_END_NAMESPACE()
852