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 <algorithm>
16 #include <functional>
17 #include <scene_plugin/api/camera_uid.h>
18 #include <scene_plugin/api/environment_uid.h>
19 #include <scene_plugin/api/light_uid.h>
20 #include <scene_plugin/api/material_uid.h>
21 #include <scene_plugin/api/mesh_uid.h>
22 #include <scene_plugin/api/node_uid.h>
23 #include <scene_plugin/api/scene_uid.h>
24 #include <scene_plugin/api/view_node_uid.h>
25 #include <scene_plugin/interface/intf_ecs_scene.h>
26 #include <scene_plugin/interface/intf_environment.h>
27
28 #include <3d/ecs/systems/intf_node_system.h>
29 #include <core/io/intf_file_manager.h>
30
31 #include <meta/api/event_handler.h>
32 #include <meta/ext/object_container.h>
33 #include <meta/interface/animation/intf_animation_controller.h>
34 #include <meta/interface/intf_content.h>
35 #include <meta/interface/intf_named.h>
36 #include <meta/interface/intf_object_hierarchy_observer.h>
37 #include <meta/interface/serialization/intf_importer.h>
38
39 #include "intf_node_private.h"
40 #include "intf_resource_private.h"
41 #include "scene_holder.h"
42 #include "task_utils.h"
43
44 // "synchronously initialize the nodes from main scene file when the scene is loaded
45
46 // Save the project .scene file when scene object is being serialized. In practice this may override the changes on
47 // scene file from other sources. To be rectified further
48
49 // Name prefab instances with IObject::GetUid(). The other option is to use fixed "prefab_instance_NN" with running
50 // instance number. The latter may cause trouble with .scene-files if it contains previous instances
51
52 using SCENE_NS::MakeTask;
53 namespace {
54 class NotifyOnExit {
55 public:
NotifyOnExit(bool notify,std::function<void ()> callback)56 explicit NotifyOnExit(bool notify, std::function<void()> callback) : notify_(notify), callback_(callback) {}
~NotifyOnExit()57 virtual ~NotifyOnExit()
58 {
59 if (notify_) {
60 callback_();
61 }
62 }
63 bool notify_;
64 std::function<void()> callback_;
65 };
66
67 class SceneImpl final : public META_NS::ObjectContainerFwd<SceneImpl, SCENE_NS::ClassId::Scene, SCENE_NS::IScene,
68 SCENE_NS::IEcsScene, META_NS::IContent, META_NS::IAnimationController> {
69 /// Add the animation controller stuff here. (as scene should be the controller of it's animations)
70 META_FORWARD_READONLY_PROPERTY(uint32_t, Count, animationController_->Count())
71 META_FORWARD_READONLY_PROPERTY(uint32_t, RunningCount, animationController_->RunningCount())
72
73 BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> GetAnimations() const override;
74 BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> GetRunning() const override;
75 bool AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation) override;
76 bool RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation) override;
77 void Clear() override;
78 StepInfo Step(const META_NS::IClock::ConstPtr& clock) override;
79 // now back to normal scene impl
80
81 META_IMPLEMENT_INTERFACE_PROPERTY(META_NS::INamed, BASE_NS::string, Name, {})
82 META_IMPLEMENT_INTERFACE_PROPERTY(
83 SCENE_NS::IScene, BASE_NS::string, SystemGraphUri, "project://assets/config/system_graph.json")
84 META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(
85 SCENE_NS::IScene, uint32_t, Status, SCENE_STATUS_UNINITIALIZED, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
86 META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IScene, SCENE_NS::INode::Ptr, RootNode, {})
87 META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, SCENE_NS::ICamera::Ptr, DefaultCamera, {})
88 META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, BASE_NS::string, Uri, {})
89 META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, bool, Asynchronous, false)
90 META_IMPLEMENT_INTERFACE_ARRAY_PROPERTY(SCENE_NS::IScene, SCENE_NS::IMaterial::Ptr, Materials, {})
91 META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IScene, SCENE_NS::IRenderConfiguration::Ptr, RenderConfiguration, {})
92
93 META_IMPLEMENT_EVENT(META_NS::IOnChanged, OnLoaded)
94 META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IEcsScene, uint8_t, RenderMode, SCENE_NS::IEcsScene::RENDER_IF_DIRTY)
95
96 META_FORWARD_READONLY_PROPERTY(IObject::Ptr, Content, (contentImpl_ ? contentImpl_->Content() : nullptr))
97 META_FORWARD_PROPERTY(bool, ContentSearchable, (contentImpl_ ? contentImpl_->ContentSearchable() : nullptr))
98 META_FORWARD_PROPERTY(
99 META_NS::IContentLoader::Ptr, ContentLoader, (contentImpl_ ? contentImpl_->ContentLoader() : nullptr))
100
101 void SetRenderMode()
102 {
103 AddEngineTask(MakeTask(
__anon3577c0ce0202(auto sceneHolder) 104 [renderMode = META_NS::GetValue(RenderMode())](auto sceneHolder) {
105 sceneHolder->SetRenderMode(renderMode);
106 return false;
107 },
108 sceneHolder_),
109 false);
110 }
111
GetCameraHandle(const SCENE_NS::ICamera::Ptr & camera)112 uint64_t GetCameraHandle(const SCENE_NS::ICamera::Ptr& camera)
113 {
114 if (camera) {
115 auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(camera);
116 if (ecsObject) {
117 return ecsObject->GetEntity().id;
118 }
119 }
120
121 return SCENE_NS::IScene::DEFAULT_CAMERA;
122 }
123
124 struct BitmapInfo {
125 BitmapInfo() = default;
BitmapInfo__anon3577c0ce0110::SceneImpl::BitmapInfo126 BitmapInfo(SCENE_NS::IBitmap::Ptr bmp) : bitmap(bmp) {}
127 BASE_NS::Math::UVec2 size {};
128 SCENE_NS::IBitmap::Ptr bitmap {};
129 META_NS::EventHandler bitmapChanged {};
130 };
131 BASE_NS::unordered_map<uint64_t, BitmapInfo> bitmaps_;
GetData(const SCENE_NS::ICamera::Ptr & camera)132 BitmapInfo& GetData(const SCENE_NS::ICamera::Ptr& camera)
133 {
134 auto cameraHandle = GetCameraHandle(camera);
135 if (cameraHandle == SCENE_NS::IScene::DEFAULT_CAMERA) {
136 cameraHandle = defaultCameraHandle_;
137 }
138 auto handle = cameraHandle;
139 if (cameraHandle == SCENE_NS::IScene::DEFAULT_CAMERA && !bitmaps_.empty()) {
140 handle = defaultCameraHandle_;
141 }
142 return bitmaps_[handle];
143 }
144
SetBitmap(const SCENE_NS::IBitmap::Ptr & bitmap,const SCENE_NS::ICamera::Ptr & camera)145 void SetBitmap(const SCENE_NS::IBitmap::Ptr& bitmap, const SCENE_NS::ICamera::Ptr& camera) override
146 {
147 BitmapInfo& data = GetData(camera);
148 auto uiBitmap = interface_pointer_cast<SCENE_NS::IBitmap>(bitmap);
149 if (!uiBitmap) {
150 // disable bitmap override.
151 data.bitmapChanged.Unsubscribe();
152 data.bitmap = {};
153 sceneHolder_->SetCameraTarget(camera, data.size, {});
154 return;
155 }
156
157 data.bitmap = uiBitmap;
158 data.size = data.bitmap->Size()->GetValue();
159 const auto rh = data.bitmap->GetRenderHandle();
160
161 data.bitmapChanged.Subscribe(
162 uiBitmap->ResourceChanged(), META_NS::MakeCallback<META_NS::IOnChanged>([this, camera]() {
163 BitmapInfo& data = GetData(camera);
164 data.size = data.bitmap->Size()->GetValue();
165 const auto rh = data.bitmap->GetRenderHandle();
166 sceneHolder_->SetCameraTarget(camera, data.size, rh);
167 }));
168
169 META_NS::Invoke<META_NS::IOnChanged>(uiBitmap->ResourceChanged());
170 }
171
GetBitmap(bool notifyFrameDrawn,const SCENE_NS::ICamera::Ptr & camera)172 SCENE_NS::IBitmap::Ptr GetBitmap(bool notifyFrameDrawn, const SCENE_NS::ICamera::Ptr& camera) override
173 {
174 BitmapInfo& data = GetData(camera);
175 if (!data.bitmap) {
176 // there is no bitmap for this.
177 // create it?
178 }
179 return data.bitmap;
180 }
181
182 #define CREATE_META_INSTANCE(type, name) GetObjectRegistry().Create<META_NS::type>(META_NS::ClassId::name)
183
184 // META_NS::IContent
SetContent(const META_NS::IObject::Ptr & content)185 bool SetContent(const META_NS::IObject::Ptr& content) override
186 {
187 // Should be no-op because the Root Node of a scene (content in this case) can be assigned only if the scene was
188 // reloaded.
189 return false;
190 }
191
Build(const META_NS::IMetadata::Ptr & data)192 bool Build(const META_NS::IMetadata::Ptr& data) override
193 {
194 auto& registry = GetObjectRegistry();
195
196 animationController_ = registry.Create<META_NS::IAnimationController>(META_NS::ClassId::AnimationController);
197
198 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
199 BASE_NS::shared_ptr<RENDER_NS::IRenderContext> rc;
200 META_NS::ITaskQueue::Ptr appQueue;
201 META_NS::ITaskQueue::Ptr engineQueue;
202
203 if (data) {
204 if (auto prp = data->GetPropertyByName<IntfPtr>("RenderContext")) {
205 rc = interface_pointer_cast<RENDER_NS::IRenderContext>(prp->GetValue());
206 }
207 if (auto prp = data->GetPropertyByName<IntfPtr>("EngineQueue")) {
208 engineQueue = interface_pointer_cast<META_NS::ITaskQueue>(prp->GetValue());
209 }
210 if (auto prp = data->GetPropertyByName<IntfPtr>("AppQueue")) {
211 appQueue = interface_pointer_cast<META_NS::ITaskQueue>(prp->GetValue());
212 }
213 }
214 if ((!rc) || (!engineQueue) || (!appQueue)) {
215 return false;
216 }
217
218 hierarchyController_ =
219 registry.Create<META_NS::IObjectHierarchyObserver>(SCENE_NS::ClassId::NodeHierarchyController);
220
221 contentImpl_ = registry.Create<META_NS::IContent>(META_NS::ClassId::ContentObject);
222 if (const auto req = interface_cast<META_NS::IRequiredInterfaces>(contentImpl_)) {
223 req->SetRequiredInterfaces({ SCENE_NS::INode::UID });
224 }
225
226 sceneHolder_.reset(new SceneHolder(GetInstanceId(), registry, rc, appQueue, engineQueue));
227 sceneHolder_->SetOperationMode(Asynchronous()->GetValue());
228
229 asyncChangedToken_ = Asynchronous()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(
230 [](const auto& sceneHolder, const auto& async) {
231 if (sceneHolder && async) {
232 sceneHolder->SetOperationMode(async->GetValue());
233 }
234 },
235 sceneHolder_, Asynchronous()));
236
237 renderModeChangedToken_ = RenderMode()->OnChanged()->AddHandler(
238 META_NS::MakeCallback<META_NS::IOnChanged>([weak = BASE_NS::weak_ptr(GetSelf())]() {
239 if (auto self = static_pointer_cast<SceneImpl>(weak.lock())) {
240 self->SetRenderMode();
241 }
242 }));
243
244 sceneHolder_->SetInitializeCallback(
245 META_NS::MakeCallback<SceneHolder::ISceneInitialized>(
246 [me = BASE_NS::weak_ptr(GetSelf())](const BASE_NS::string& rootId, const BASE_NS::string& cameraId) {
247 if (auto self = me.lock().get())
248 static_cast<SceneImpl*>(self)->onSceneInitialized(rootId, cameraId);
249 }),
250 sceneHolder_);
251 sceneHolder_->SetSceneLoadedCallback(
252 META_NS::MakeCallback<SceneHolder::ISceneLoaded>([me = BASE_NS::weak_ptr(GetSelf())](uint32_t status) {
253 if (auto self = me.lock().get())
254 static_cast<SceneImpl*>(self)->OnSceneLoaded(status);
255 }),
256 sceneHolder_);
257
258 sceneHolder_->Initialize(sceneHolder_);
259 sceneHolder_->SetSystemGraphUri(META_NS::GetValue(SystemGraphUri()));
260 SubscribeToPropertyChanges();
261
262 return true;
263 }
264
265 // this is not usually needed as explicit action, but the scene will pickup changes from
266 // ScenePresenters on the fly
OnCameraChanged()267 void OnCameraChanged()
268 {
269 // Async, sceneholder takes over
270 if (sceneHolder_) {
271 sceneHolder_->ChangeCamera(META_ACCESS_PROPERTY(DefaultCamera)->GetValue());
272 }
273 }
274
275 // Async, sceneholder takes over
276 BASE_NS::vector<META_NS::IAnimation::Ptr> allAnims_;
GetAnimations()277 BASE_NS::vector<META_NS::IAnimation::Ptr> GetAnimations() override
278 {
279 // adds/removes animations to cache..
280 auto tmp = allAnims_;
281 allAnims_.clear();
282 for (auto anim : sceneHolder_->GetAnimations()) {
283 if (auto i = interface_cast<META_NS::IObject>(anim)) {
284 auto name = i->GetName();
285
286 size_t ix = 0;
287 if (!RollOverPrefix(ix, name, ANIMATIONS_PREFIX)) {
288 continue;
289 }
290
291 // check cache
292 META_NS::IAnimation::Ptr rn;
293 auto subname = name.substr(ix);
294 for (auto& a : tmp) {
295 if (interface_cast<META_NS::IObject>(a)->GetName() == subname) {
296 rn = a;
297 break;
298 }
299 }
300 if (!rn) {
301 rn = interface_pointer_cast<META_NS::IAnimation>(
302 CreateNode(name.substr(ix), false, SCENE_NS::ClassId::Animation.Id(),
303 SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
304 }
305
306 allAnims_.push_back(rn);
307 }
308 }
309 return allAnims_;
310 }
311
GetAnimation(const BASE_NS::string_view name)312 META_NS::IAnimation::Ptr GetAnimation(const BASE_NS::string_view name) override
313 {
314 #ifndef USE_DIRECT_ECS_ANIMATION
315
316 size_t ix = 0;
317 if (!RollOverPrefix(ix, name, ANIMATIONS_PREFIX)) {
318 return { nullptr };
319 }
320
321 // check cache
322 if (auto it = animations_.find(name.substr(ix)); it != animations_.end()) {
323 if (auto animation = it->second.lock()) {
324 return animation;
325 }
326 }
327 // Create EcsObject. When running asynchronously, we have no way of knowing if we can rely that existing
328 // animation will be found
329 auto anim = interface_pointer_cast<META_NS::IAnimation>(CreateNode(name.substr(ix), false,
330 SCENE_NS::ClassId::Animation.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
331
332 return anim;
333 #else
334 auto ecsAnimation = sceneHolder_->GetAnimation(BASE_NS::string(name.data(), name.size()));
335
336 if (!ecsAnimation) {
337 ecsAnimation = GetObjectRegistry().Create<SCENE_NS::IEcsAnimation>(SCENE_NS::ClassId::EcsAnimation);
338 AddEngineTask(
339 META_NS::MakeCallable<META_NS::ITaskQueueTask>(
340 [ecsAnimation, nameString = BASE_NS::string(name.data(), name.size()),
341 weak = BASE_NS::weak_ptr(sceneHolder_)]() {
342 if (auto sceneHolder = weak.lock()) {
343 CORE_NS::Entity entity;
344 if (sceneHolder->FindAnimation(nameString, entity)) {
345 if (auto ecsProxyIf = interface_pointer_cast<SCENE_NS::IEcsProxyObject>(ecsAnimation)) {
346 ecsProxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
347 }
348 ecsAnimation->SetEntity(*sceneHolder->GetEcs(), entity);
349 }
350 }
351 return false;
352 }),
353 false);
354 }
355 META_ACCESS_PROPERTY(Animations)->Get()->Add(ecsAnimation);
356
357 return interface_pointer_cast<META_NS::IAnimation>(ecsAnimation);
358 #endif
359 }
360
CreateEmpty()361 void CreateEmpty() override
362 {
363 Load("scene://empty");
364 }
365
Load(const BASE_NS::string_view uri)366 bool Load(const BASE_NS::string_view uri) override
367 {
368 if (!Name()->IsValueSet()) {
369 SetValue(Name(), "Scene");
370 }
371
372 if (!uri.empty()) {
373 META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_LOADING);
374 sceneHolder_->Load(BASE_NS::string(uri.data(), uri.size()));
375 return true;
376 }
377 return false;
378 }
379
onSystemGraphUriChanged()380 void onSystemGraphUriChanged()
381 {
382 META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_LOADING);
383 sceneHolder_->SetSystemGraphUri(META_NS::GetValue(SystemGraphUri()));
384 }
385
SetRenderSize(uint32_t width,uint32_t height,const SCENE_NS::ICamera::Ptr & camera)386 void SetRenderSize(uint32_t width, uint32_t height, const SCENE_NS::ICamera::Ptr& camera) override
387 {
388 if (camera) {
389 camera->SetDefaultRenderTargetSize(width, height);
390 } else if (auto defaultCamera = META_NS::GetValue(DefaultCamera())) {
391 defaultCamera->SetDefaultRenderTargetSize(width, height);
392 }
393 }
394
GetNode(const BASE_NS::string_view path,const BASE_NS::Uid classId,SCENE_NS::INode::BuildBehavior buildBehavior)395 SCENE_NS::INode::Ptr GetNode(const BASE_NS::string_view path, const BASE_NS::Uid classId,
396 SCENE_NS::INode::BuildBehavior buildBehavior) override
397 {
398 return GetNodeRecursive(path, classId, true, buildBehavior);
399 }
400
ResolveNodeTypeFromPath(const BASE_NS::string_view patchedPath,bool isNodeType)401 META_NS::ObjectId ResolveNodeTypeFromPath(const BASE_NS::string_view patchedPath, bool isNodeType)
402 {
403 // This is best effort
404 // We cannot determine the type unless ECS has probed the component
405
406 // This kind of introspection may cause materials and meshes to be treated as nodes
407 // which kind of contradicts with their normal use through API
408 auto ecs = GetEcs();
409 CORE3D_NS::INodeSystem& nodeSystem = *CORE_NS::GetSystem<CORE3D_NS::INodeSystem>(*ecs);
410 const auto& root = nodeSystem.GetRootNode();
411
412 const auto& ecsNode = root.LookupNodeByPath(patchedPath);
413 CORE_NS::Entity entity {};
414 if (ecsNode) {
415 entity = ecsNode->GetEntity();
416 } else {
417 CORE_LOG_W("%s:No entity for %s, type info not available", __func__, BASE_NS::string(patchedPath).c_str());
418 CORE_LOG_W("If you know the expected type, consider using a template or providing the class id");
419 }
420
421 // shadow camera could provide false positive, so order matters
422 if (auto lightManager = ecs->GetComponentManager(CORE3D_NS::ILightComponentManager::UID)) {
423 if (lightManager->HasComponent(entity)) {
424 return SCENE_NS::ClassId::Light;
425 }
426 }
427
428 if (auto cameraManager = ecs->GetComponentManager(CORE3D_NS::ICameraComponentManager::UID)) {
429 if (cameraManager->HasComponent(entity)) {
430 return SCENE_NS::ClassId::Camera;
431 }
432 }
433
434 if (auto envManager = ecs->GetComponentManager(CORE3D_NS::IEnvironmentComponentManager::UID)) {
435 if (envManager->HasComponent(entity)) {
436 return SCENE_NS::ClassId::Environment;
437 }
438 }
439
440 if (!isNodeType) {
441 if (auto nodeManager = ecs->GetComponentManager(CORE3D_NS::INodeComponentManager::UID)) {
442 // quirk, prefer nodes with node component treated as node)
443 if (!nodeManager->HasComponent(entity)) {
444 if (auto meshManager = ecs->GetComponentManager(CORE3D_NS::IMeshComponentManager::UID)) {
445 if (meshManager->HasComponent(entity)) {
446 return SCENE_NS::ClassId::Mesh;
447 }
448 }
449 if (auto materialManager = ecs->GetComponentManager(CORE3D_NS::IMaterialComponentManager::UID)) {
450 if (materialManager->HasComponent(entity)) {
451 return SCENE_NS::ClassId::Material;
452 }
453 }
454 }
455 }
456 }
457
458 return SCENE_NS::ClassId::Node;
459 }
460
GetNodeRecursive(const BASE_NS::string_view path,const META_NS::ObjectId classId,bool recurse,SCENE_NS::INode::BuildBehavior buildBehavior)461 SCENE_NS::INode::Ptr GetNodeRecursive(const BASE_NS::string_view path, const META_NS::ObjectId classId,
462 bool recurse, SCENE_NS::INode::BuildBehavior buildBehavior)
463 {
464 if (path.empty() || path == "/") {
465 return SCENE_NS::INode::Ptr {};
466 }
467
468 BASE_NS::string patchedPath = recurse ? NormalizePath(path) : BASE_NS::string(path.data(), path.size());
469 if (auto ite = nodes_.find(patchedPath) != nodes_.cend()) {
470 return nodes_[patchedPath];
471 }
472
473 // ensure parent objects exist
474 if (recurse) {
475 size_t ix = patchedPath.find('/', 1);
476 while (BASE_NS::string_view::npos != ix) {
477 auto substr = patchedPath.substr(0, ix);
478 // When we traverse up the tree, we must ensure that the object is a node.
479 currentParent_ = GetNodeRecursive(substr, SCENE_NS::INode::UID, false, buildBehavior);
480 ++ix;
481 ix = patchedPath.find('/', ix);
482 }
483 }
484
485 auto ecs = GetEcs();
486 META_NS::ObjectId implementationId = SCENE_NS::ClassId::Node;
487
488 bool isNodeType = (classId == SCENE_NS::INode::UID);
489 if ((classId == META_NS::IObject::UID || isNodeType) && ecs) {
490 implementationId = ResolveNodeTypeFromPath(patchedPath, isNodeType);
491
492 } else {
493 implementationId = classId;
494 }
495
496 auto node = CreateNode(patchedPath, false, implementationId, buildBehavior);
497
498 currentParent_ = {};
499 return node; // finalInterface;
500 }
501
RemoveNodeFromCurrentContainer(SCENE_NS::INode::Ptr & node)502 void RemoveNodeFromCurrentContainer(SCENE_NS::INode::Ptr& node)
503 {
504 if (auto containable = interface_cast<META_NS::IContainable>(node)) {
505 if (auto parent = interface_pointer_cast<META_NS::IContainer>(containable->GetParent())) {
506 parent->Remove(node);
507 }
508 }
509 }
510
RollOverPrefix(size_t & ix,const BASE_NS::string_view & name,const BASE_NS::string_view & prefix)511 bool RollOverPrefix(size_t& ix, const BASE_NS::string_view& name, const BASE_NS::string_view& prefix)
512 {
513 while (ix < name.length() && name[ix] == '/') {
514 ix++;
515 }
516
517 if (name.substr(ix).find(prefix) == 0) {
518 ix += prefix.length();
519 }
520
521 while (ix < name.length() && name[ix] == '/') {
522 ix++;
523 }
524
525 return ix < name.length();
526 }
527
AddMaterial(SCENE_NS::IMaterial::Ptr material)528 void AddMaterial(SCENE_NS::IMaterial::Ptr material) override
529 {
530 auto materials = Materials()->GetValue();
531 auto it = std::find(materials.begin(), materials.end(), material);
532 if (it != materials.end()) {
533 // Already exists.
534 CORE_LOG_D("Trying to add same material to scene multiple times.");
535 return;
536 }
537
538 Materials()->AddValue(material);
539 UpdateCachedReference(interface_pointer_cast<SCENE_NS::INode>(material));
540 }
541
RemoveMaterial(SCENE_NS::IMaterial::Ptr material)542 void RemoveMaterial(SCENE_NS::IMaterial::Ptr material) override
543 {
544 auto lock = Materials().GetLockedAccess();
545 auto vec = lock->GetValue();
546 for (size_t index = 0; index != vec.size(); ++index) {
547 if (vec[index] == material) {
548 lock->RemoveAt(index);
549 break;
550 }
551 }
552
553 for (auto&& ite : materials_) {
554 if (ite.second.lock() == material) {
555 ReleaseMaterial(ite.first);
556 break;
557 }
558 }
559 }
560
GetCameras() const561 BASE_NS::vector<SCENE_NS::ICamera::Ptr> GetCameras() const override
562 {
563 BASE_NS::vector<SCENE_NS::ICamera::Ptr> result;
564 for (auto c : cameras_) {
565 if (auto cam = c.second.lock()) {
566 result.push_back(cam);
567 }
568 }
569 return result;
570 }
571
GetMaterials() const572 BASE_NS::vector<SCENE_NS::IMaterial::Ptr> GetMaterials() const override
573 {
574 BASE_NS::vector<SCENE_NS::IMaterial::Ptr> result;
575 for (auto& material : materials_) {
576 auto ptr = material.second.lock();
577 if (ptr) {
578 result.push_back(ptr);
579 }
580 }
581
582 return result;
583 }
584
585 // Returns a material from the scene with a given path
GetMaterial(const BASE_NS::string_view name)586 SCENE_NS::IMaterial::Ptr GetMaterial(const BASE_NS::string_view name) override
587 {
588 // The material file, aka uri-path is somewhat parallel due ownership is different for uri-materials
589 // however, one can claim traditional handle and have them preserved
590 // through flat cache. Should be consolidated someday.
591 if (name.find("://") != BASE_NS::string_view::npos) {
592 return GetOrLoadMaterial(name);
593 }
594
595 size_t ix = 0;
596 if (!RollOverPrefix(ix, name, MATERIALS_PREFIX)) {
597 return { nullptr };
598 }
599
600 // check cache (first with name:entityid)
601 if (auto it = materials_.find(name.substr(ix)); it != materials_.end()) {
602 if (auto material = it->second.lock()) {
603 return material;
604 }
605 }
606
607 // check cache (direct material name)
608 for (auto entry : materials_) {
609 auto material = entry.second.lock();
610 if (auto node = interface_pointer_cast<SCENE_NS::INode>(material)) {
611 if (node->Name()->GetValue() == name) {
612 return material;
613 }
614 }
615 }
616
617 if (auto it = materials_.find(name.substr(ix)); it != materials_.end()) {
618 if (auto material = it->second.lock()) {
619 return material;
620 }
621 }
622
623 // Create EcsObject. When running asynchronously, we have no way of knowing if we should create
624 // new node or just rely that existing will be found
625 auto mat = interface_pointer_cast<SCENE_NS::IMaterial>(CreateNode(name.substr(ix), false,
626 SCENE_NS::ClassId::Material.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
627
628 return mat;
629 }
630
GetMeshes() const631 BASE_NS::vector<SCENE_NS::IMesh::Ptr> GetMeshes() const override
632 {
633 BASE_NS::vector<SCENE_NS::IMesh::Ptr> result;
634 for (auto mesh : meshes_) {
635 auto ptr = mesh.second.lock();
636 if (ptr) {
637 result.push_back(ptr);
638 }
639 }
640
641 return result;
642 }
643
644 // Returns a material from the scene with a given name
GetMesh(const BASE_NS::string_view name)645 SCENE_NS::IMesh::Ptr GetMesh(const BASE_NS::string_view name) override
646 {
647 size_t ix = 0;
648 if (!RollOverPrefix(ix, name, MESHES_PREFIX)) {
649 return { nullptr };
650 }
651
652 // check cache
653 if (auto it = meshes_.find(name.substr(ix)); it != meshes_.end()) {
654 if (auto mesh = it->second.lock()) {
655 return mesh;
656 }
657 }
658 // Create EcsObject. When running asynchronously, we have no way of knowing if we should create
659 // new node or just rely that existing will be found
660 auto mesh = interface_pointer_cast<SCENE_NS::IMesh>(CreateNode(name.substr(ix), false,
661 SCENE_NS::ClassId::Mesh.Id(), SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
662
663 return mesh;
664 }
665
constructPath(CORE_NS::Entity ent) const666 BASE_NS::string constructPath(CORE_NS::Entity ent) const
667 {
668 auto ecs = GetEcs();
669 auto* nodeManager = CORE_NS::GetManager<CORE3D_NS::INodeComponentManager>(*ecs);
670 auto* nameManager = CORE_NS::GetManager<CORE3D_NS::INameComponentManager>(*ecs);
671 BASE_NS::string path;
672 if (nodeManager && nameManager) {
673 auto curent = ent;
674 for (;;) {
675 if (!CORE_NS::EntityUtil::IsValid(curent)) {
676 // Reached root.
677 break;
678 }
679 if (!nodeManager->HasComponent(curent)) {
680 // not a node?
681 return "";
682 }
683 if (!nameManager->HasComponent(curent)) {
684 // no name in hierarchy. "fail"? or generate "name" for path..
685 return "";
686 }
687 auto namecomp = nameManager->Get(curent);
688 if (!path.empty()) {
689 path.insert(0, "/");
690 }
691 path.insert(0, namecomp.name.c_str());
692 const auto& node = nodeManager->Get(curent);
693 curent = node.parent;
694 }
695 }
696 if (!path.empty()) {
697 path.insert(0, "/");
698 }
699 return path;
700 }
701 // Implementation for scene change callbacks.
702 // The tricky bit here is that we may have placeholders on containers that may have received serialized data
703 // during construction, so we just cannot replace those, but need to rebind them instead
onSceneInitialized(const BASE_NS::string & rootId,const BASE_NS::string & cameraId)704 void onSceneInitialized(const BASE_NS::string& rootId, const BASE_NS::string& cameraId)
705 {
706 // Set root node
707 rootNodeId_ = rootId;
708 auto rootIdNormalized = NormalizePath(rootNodeId_);
709
710 if (rootNodePtr_) {
711 // check if someone assigned us a root node while we are preserving the previous one
712 if (!META_ACCESS_PROPERTY(RootNode)->GetValue()) {
713 META_ACCESS_PROPERTY(RootNode)->SetValue(rootNodePtr_);
714 }
715 rootNodePtr_.reset();
716 }
717
718 if (cameraNodePtr_) {
719 // check if someone assigned us a root node while we are preserving the previous one
720 if (!META_ACCESS_PROPERTY(DefaultCamera)->GetValue()) {
721 META_ACCESS_PROPERTY(DefaultCamera)->SetValue(cameraNodePtr_);
722 }
723 cameraNodePtr_.reset();
724 }
725
726 if (auto renderConfiguration = META_NS::GetValue(RenderConfiguration())) {
727 // Ensure the render configuration is bound to scene.
728 auto resource = interface_pointer_cast<IResourcePrivate>(renderConfiguration);
729 if (resource) {
730 resource->Connect(sceneHolder_);
731 }
732 }
733
734 // setup subscription for toolkit changes
735 RenderConfiguration()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
736 auto resource = interface_pointer_cast<IResourcePrivate>(GetValue(RenderConfiguration()));
737 if (resource) {
738 resource->Connect(sceneHolder_);
739 }
740 }),
741 reinterpret_cast<uint64_t>(this));
742
743 if (auto rootNode = META_ACCESS_PROPERTY(RootNode)->GetValue()) {
744 // switch new ecsObject
745 BindNodeToEcs(rootNode, rootIdNormalized, false);
746 nodes_[rootIdNormalized] = rootNode;
747 } else {
748 META_ACCESS_PROPERTY(RootNode)->SetValue(GetSelf<SCENE_NS::IScene>()->GetNode<SCENE_NS::INode>(rootId));
749 }
750
751 auto defaultCamera = META_ACCESS_PROPERTY(DefaultCamera)->GetValue();
752 if (!defaultCamera) {
753 defaultCamera = GetSelf<SCENE_NS::IScene>()->GetNode<SCENE_NS::ICamera>(cameraId);
754 META_ACCESS_PROPERTY(DefaultCamera)->SetValue(defaultCamera);
755 }
756
757 if (defaultCamera) {
758 ActivateCamera(defaultCamera);
759 }
760
761 // under normal conditions, this would take place asyncronously
762 // however, if we are running on synchronous mode we need to restore the ecs bindings here
763 for (auto& prevNode : nodes_) {
764 if (prevNode.first != rootIdNormalized && prevNode.first != cameraId) {
765 BindNodeToEcs(prevNode.second, prevNode.first, false);
766 }
767 }
768
769 // This is information only unless we instantiate all the scene nodes immediately
770 // Should be flagged more effectively on production builds
771 CORE3D_NS::INodeSystem& nodeSystem = *CORE_NS::GetSystem<CORE3D_NS::INodeSystem>(*GetEcs());
772 const auto& root = nodeSystem.GetRootNode();
773
774 instantiateNodes(root, "/");
775
776 // notify observers about the change
777 META_ACCESS_PROPERTY(Status)->SetValue(SCENE_NS::IScene::SCENE_STATUS_READY);
778 META_NS::Invoke<META_NS::IOnChanged>(OnLoaded());
779
780 sceneHolder_->SetUninitializeCallback(
781 META_NS::MakeCallback<SceneHolder::ISceneUninitialized>([me = BASE_NS::weak_ptr(GetSelf())]() {
782 if (auto self = me.lock().get())
783 static_cast<SceneImpl*>(self)->DetachScene();
784 }),
785 sceneHolder_);
786 }
787
OnSceneLoaded(uint32_t status)788 void OnSceneLoaded(uint32_t status)
789 {
790 META_ACCESS_PROPERTY(Status)->SetValue(status);
791 if (status == SCENE_NS::IScene::SCENE_STATUS_READY) {
792 META_NS::Invoke<META_NS::IOnChanged>(OnLoaded());
793 }
794 }
795
796 // Implementation for scene processing.
797
DetachScene(bool setDirty=false)798 void DetachScene(bool setDirty = false)
799 {
800 if (META_ACCESS_PROPERTY(Status)) {
801 META_ACCESS_PROPERTY(Status)->SetValue(SCENE_STATUS_UNINITIALIZED);
802 }
803
804 cameras_.clear();
805 materials_.clear();
806 meshes_.clear();
807 animations_.clear();
808 nodes_.clear();
809
810 // This effectively means that we keep the scene user objects forcibly live
811 // until we get fresh ones to replace them, not sure if that is intended
812 rootNodePtr_ = META_ACCESS_PROPERTY(RootNode)->GetValue();
813 cameraNodePtr_ = META_ACCESS_PROPERTY(DefaultCamera)->GetValue();
814 META_ACCESS_PROPERTY(RootNode)->SetValue({});
815 META_ACCESS_PROPERTY(DefaultCamera)->SetValue({});
816
817 if (setDirty) {
818 for (const auto& bitmap : bitmaps_) {
819 /*auto externalBitmap = interface_pointer_cast<UI_NS::ILume2DExternalBitmap>(bitmap.second.bitmap);
820 if (externalBitmap) {
821 externalBitmap->SetCoreBitmap(nullptr);
822 }*/
823 bitmap.second.bitmap->SetRenderHandle({}, { 0, 0 });
824 }
825 }
826 }
827
instantiateNodes(const CORE3D_NS::ISceneNode & node,BASE_NS::string path)828 void instantiateNodes(const CORE3D_NS::ISceneNode& node, BASE_NS::string path)
829 {
830 if (!path.empty() && path.back() != '/') {
831 path.append("/");
832 }
833 if (!node.GetName().empty()) {
834 path.append(node.GetName());
835 } else {
836 #ifndef INSTANTIATE_NODES_ON_INITIALIZE
837 path.append("[");
838 path.append(BASE_NS::to_string(node.GetEntity().id));
839 path.append("]");
840 #endif
841 }
842 SCENE_PLUGIN_VERBOSE_LOG("%s", path.c_str());
843 #ifdef INSTANTIATE_NODES_ON_INITIALIZE
844 GetNode(path);
845 #endif
846
847 for (const auto child : node.GetChildren())
848 instantiateNodes(*child, path);
849 }
850
NormalizePath(const BASE_NS::string_view & path)851 BASE_NS::string NormalizePath(const BASE_NS::string_view& path)
852 {
853 BASE_NS::string patchedPath;
854 if (path == rootNodeId_) {
855 patchedPath = path;
856 patchedPath.insert(0, "/");
857 } else {
858 bool hasSlash = (path[0] == '/');
859 if (path.compare(hasSlash ? 1 : 0, rootNodeId_.size(), rootNodeId_.data())) {
860 SCENE_PLUGIN_VERBOSE_LOG("%s: the path does not contain root node, patching the path", __func__);
861 patchedPath.append("/");
862 patchedPath.append(rootNodeId_);
863 if (!hasSlash) {
864 patchedPath.append("/");
865 }
866 patchedPath.append(path.data(), path.size());
867 } else {
868 patchedPath = path;
869 if (!hasSlash) {
870 patchedPath = "/" + patchedPath;
871 }
872 }
873 }
874 return patchedPath;
875 }
876
FindEcsObject(const BASE_NS::string_view & path,BASE_NS::string & patchedPath)877 SCENE_NS::IEcsObject::Ptr FindEcsObject(const BASE_NS::string_view& path, BASE_NS::string& patchedPath)
878 {
879 patchedPath = NormalizePath(path);
880
881 if (auto it = nodes_.find(patchedPath); it != nodes_.end()) {
882 return interface_pointer_cast<SCENE_NS::IEcsObject>(it->second);
883 }
884 return SCENE_NS::IEcsObject::Ptr {};
885 }
886
CreateNewEcsObject(const BASE_NS::string &)887 SCENE_NS::IEcsObject::Ptr CreateNewEcsObject(const BASE_NS::string& /*path*/)
888 {
889 // Create new helper object
890 // Not much left here, could be presumably removed
891 auto ret = SCENE_NS::IEcsObject::Ptr {};
892
893 // Allow construction to progress even ecs was not available,
894 // nodes will perform initialization asynchronously anyway
895 if (auto object = META_NS::GetObjectRegistry().Create(SCENE_NS::ClassId::EcsObject)) {
896 ret = interface_pointer_cast<SCENE_NS::IEcsObject>(object);
897 }
898 return ret;
899 }
900
GetEcsObject(const BASE_NS::string_view & path)901 SCENE_NS::IEcsObject::Ptr GetEcsObject(const BASE_NS::string_view& path) override
902 {
903 return interface_pointer_cast<SCENE_NS::IEcsObject>(
904 GetNode(path, META_NS::IObject::UID, SCENE_NS::INode::BuildBehavior::NODE_BUILD_CHILDREN_NO_BUILD));
905 }
906
GetEcs()907 CORE_NS::IEcs::Ptr GetEcs() override
908 {
909 return sceneHolder_->GetEcs();
910 }
911
GetEcs() const912 CORE_NS::IEcs::Ptr GetEcs() const
913 {
914 return sceneHolder_->GetEcs();
915 }
916
AddEngineTask(const META_NS::ITaskQueue::CallableType::Ptr & task,bool runDeferred)917 META_NS::ITaskQueue::Token AddEngineTask(
918 const META_NS::ITaskQueue::CallableType::Ptr& task, bool runDeferred) override
919 {
920 if (sceneHolder_) {
921 return sceneHolder_->QueueEngineTask(task, runDeferred);
922 }
923 return META_NS::ITaskQueue::Token {};
924 }
925
AddApplicationTask(const META_NS::ITaskQueue::CallableType::Ptr & task,bool runDeferred)926 META_NS::ITaskQueue::Token AddApplicationTask(
927 const META_NS::ITaskQueue::CallableType::Ptr& task, bool runDeferred) override
928 {
929 if (sceneHolder_) {
930 return sceneHolder_->QueueApplicationTask(task, runDeferred);
931 }
932 return META_NS::ITaskQueue::Token {};
933 }
934
CancelEngineTask(META_NS::ITaskQueue::Token token)935 void CancelEngineTask(META_NS::ITaskQueue::Token token) override
936 {
937 if (sceneHolder_) {
938 return sceneHolder_->CancelEngineTask(token);
939 }
940 }
941
CancelAppTask(META_NS::ITaskQueue::Token token)942 void CancelAppTask(META_NS::ITaskQueue::Token token) override
943 {
944 if (sceneHolder_) {
945 return sceneHolder_->CancelAppTask(token);
946 }
947 }
948
GetEntityCollection()949 SCENE_NS::IEntityCollection* GetEntityCollection() override
950 {
951 if (sceneHolder_) {
952 return sceneHolder_->GetEntityCollection();
953 }
954
955 return {};
956 }
957
GetAssetManager()958 SCENE_NS::IAssetManager* GetAssetManager() override
959 {
960 if (sceneHolder_) {
961 return sceneHolder_->GetAssetManager();
962 }
963
964 return {};
965 }
966
GetOrLoadMaterial(BASE_NS::string_view uri)967 SCENE_NS::IMaterial::Ptr GetOrLoadMaterial(BASE_NS::string_view uri)
968 {
969 // check cache
970 if (auto it = uriMaterials_.find(uri); it != uriMaterials_.end()) {
971 if (auto material = it->second.lock()) {
972 return material;
973 }
974 }
975
976 auto ret = LoadMaterial(uri);
977 uriMaterials_[uri] = ret;
978 return ret;
979 }
980
LoadMaterial(BASE_NS::string_view uri)981 SCENE_NS::IMaterial::Ptr LoadMaterial(BASE_NS::string_view uri) override
982 {
983 bool isGltfUri = (uri.find(".glb/materials/") != BASE_NS::string_view::npos) ||
984 (uri.find(".gltf/materials/") != BASE_NS::string_view::npos);
985 if (isGltfUri) {
986 auto resource = CreateResourceFromUri(SCENE_NS::ClassId::Material, uri);
987 if (resource) {
988 return interface_pointer_cast<SCENE_NS::IMaterial>(resource);
989 }
990
991 CORE_LOG_W("Could not resolve material URI: %s", BASE_NS::string(uri.data(), uri.size()).c_str());
992 // we could return the control to CreateMaterial and let the engine resolve the materials with uri
993 // componenta as they seem to be in place when loaded from glb or gltf
994 }
995
996 SCENE_NS::IMaterial::Ptr ret;
997
998 return ret;
999 }
1000
CreateNode(const META_NS::ObjectId classId)1001 SCENE_NS::INode::Ptr CreateNode(const META_NS::ObjectId classId)
1002 {
1003 SCENE_NS::INode::Ptr node;
1004
1005 if (classId == SCENE_NS::ClassId::Light.Id() || classId == SCENE_NS::ILight::UID) {
1006 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Light);
1007 } else if (classId == SCENE_NS::ClassId::Camera.Id() || classId == SCENE_NS::ICamera::UID) {
1008 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Camera);
1009 } /*else if (classId == SCENE_NS::ClassId::ViewNode.Id() || classId == SCENE_NS::IViewNode::UID) {
1010 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::ViewNode);
1011 } */
1012 else if (classId == SCENE_NS::ClassId::Material.Id() || classId == SCENE_NS::IMaterial::UID) {
1013 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Material);
1014 } else if (classId == SCENE_NS::ClassId::Mesh.Id() || classId == SCENE_NS::IMesh::UID) {
1015 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Mesh);
1016 } else if (classId == SCENE_NS::ClassId::Animation.Id() || classId == META_NS::IAnimation::UID) {
1017 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Animation);
1018 } else if (classId == SCENE_NS::ClassId::Environment.Id() || classId == SCENE_NS::IEnvironment::UID) {
1019 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Environment);
1020 } else {
1021 if (classId != META_NS::IObject::UID && classId != SCENE_NS::ClassId::Node.Id() &&
1022 classId != SCENE_NS::INode::UID) {
1023 CORE_LOG_W("%s: uid not known, returning INode instance", __func__);
1024 }
1025
1026 node = GetObjectRegistry().Create<SCENE_NS::INode>(SCENE_NS::ClassId::Node);
1027 }
1028
1029 return node;
1030 }
1031
1032 // Quirk / constraints:
1033 // 1) has name -> cached and asynchronously initialized
1034 // 2) name empty, just an object, not sure if needed at the end
CreateNode(const BASE_NS::string_view name,bool createEngineObject,const META_NS::ObjectId classId,SCENE_NS::INode::BuildBehavior buildBehavior)1035 SCENE_NS::INode::Ptr CreateNode(const BASE_NS::string_view name, bool createEngineObject,
1036 const META_NS::ObjectId classId, SCENE_NS::INode::BuildBehavior buildBehavior) override
1037 {
1038 if (const auto& ite = nodes_.find(name); ite != nodes_.cend() && createEngineObject) {
1039 CORE_LOG_W("Refusing to create new duplicate node: %s", BASE_NS::string(name.data(), name.size()).c_str());
1040 return ite->second;
1041 }
1042
1043 SCENE_NS::INode::Ptr node = CreateNode(classId);
1044
1045 if (node) {
1046 node->BuildChildren(buildBehavior);
1047 BindNodeToEcs(node, name, createEngineObject);
1048 }
1049 return node;
1050 }
1051
CreateResourceFromUri(const META_NS::ObjectId classId,BASE_NS::string_view uri)1052 SCENE_NS::INode::Ptr CreateResourceFromUri(const META_NS::ObjectId classId, BASE_NS::string_view uri)
1053 {
1054 SCENE_NS::INode::Ptr node;
1055
1056 auto entity = sceneHolder_->GetEntityByUri(uri);
1057
1058 if (CORE_NS::EntityUtil::IsValid(entity)) {
1059 BASE_NS::string name;
1060 if (sceneHolder_->GetEntityName(entity, name)) {
1061 node = CreateNode(classId);
1062 if (node) {
1063 auto ecsScene = GetSelf<SCENE_NS::IEcsScene>();
1064 auto ecsObject = CreateNewEcsObject({});
1065 auto nodeInterface = interface_pointer_cast<INodeEcsInterfacePrivate>(node);
1066 nodeInterface->Initialize(ecsScene, ecsObject, {}, "", name, sceneHolder_, entity);
1067 }
1068 }
1069 }
1070
1071 return node;
1072 }
1073
BindNodeToEcs(SCENE_NS::INode::Ptr & node,const BASE_NS::string_view fullPath,bool createEngineObject)1074 void BindNodeToEcs(
1075 SCENE_NS::INode::Ptr& node, const BASE_NS::string_view fullPath, bool createEngineObject) override
1076 {
1077 SCENE_PLUGIN_VERBOSE_LOG("Scene::BindNodeToEcs called for %s", BASE_NS::string(fullPath).c_str());
1078
1079 CORE_NS::Entity entity;
1080 auto ecsScene = GetSelf<SCENE_NS::IEcsScene>();
1081
1082 bool addToRootContainer { false };
1083 BASE_NS::string nodePath;
1084 BASE_NS::string nodeName;
1085
1086 auto classUid = interface_cast<META_NS::IObject>(node)->GetClassId();
1087 bool isResourceClassType = (classUid == SCENE_NS::ClassId::Material) || // Material
1088 (classUid == SCENE_NS::ClassId::Mesh) || // Mesh
1089 (classUid == SCENE_NS::ClassId::Animation); // Animation
1090
1091 auto isRealUri = fullPath.find("://") != BASE_NS::string_view::npos;
1092
1093 if (!isRealUri) {
1094 auto cutIx = fullPath.find_last_of('/');
1095 if (cutIx != BASE_NS::string_view::npos && cutIx < fullPath.size()) {
1096 ++cutIx;
1097 nodePath = BASE_NS::string(fullPath.data(), cutIx);
1098 nodeName = BASE_NS::string(fullPath.data() + cutIx, fullPath.size() - cutIx);
1099 } else if (!fullPath.empty()) {
1100 nodeName = BASE_NS::string(fullPath.data(), fullPath.size());
1101 }
1102 }
1103
1104 if (nodeName.empty()) {
1105 if (createEngineObject) {
1106 nodeName = interface_cast<META_NS::IObjectInstance>(node)->GetInstanceId().ToString();
1107 } else {
1108 if (isRealUri) {
1109 auto ecsObject = CreateNewEcsObject({});
1110 auto nodeInterface = interface_cast<INodeEcsInterfacePrivate>(node);
1111 nodeInterface->Initialize(ecsScene, ecsObject, {}, "", BASE_NS::string(fullPath), sceneHolder_, {});
1112 } else {
1113 CORE_LOG_W("%s: refusing to create proxy object without valid target, name is empty.", __func__);
1114 }
1115 return;
1116 }
1117 }
1118
1119 auto nodeInterface = interface_cast<INodeEcsInterfacePrivate>(node);
1120
1121 if (nodeInterface->EcsObject() && CORE_NS::EntityUtil::IsValid(nodeInterface->EcsObject()->GetEntity())) {
1122 CORE_LOG_W("%s: refusing to recreate proxy object while current one is valid: %s", __func__,
1123 BASE_NS::string(fullPath).c_str());
1124 return;
1125 }
1126
1127 auto ecsObject = CreateNewEcsObject({});
1128
1129 // NEW NODE
1130 if (createEngineObject) {
1131 auto classUid = interface_pointer_cast<META_NS::IObject>(node)->GetClassId();
1132 auto constructNode = [this, nodePath, nodeName, classUid](
1133 const auto& sceneHolder) -> CORE_NS::Entity {
1134 CORE_NS::Entity entity;
1135 if (sceneHolder) {
1136 // Should not need to type this deep here
1137 if (classUid == SCENE_NS::ClassId::Material) {
1138 entity = sceneHolder->CreateMaterial(nodeName);
1139 } else if (classUid == SCENE_NS::ClassId::Camera) {
1140 entity = sceneHolder->CreateCamera(nodePath, nodeName, 0);
1141 } else if (auto node = sceneHolder->CreateNode(nodePath, nodeName)) {
1142 entity = node->GetEntity();
1143
1144 sceneHolder->EnableLayerComponent(entity);
1145 if (classUid == SCENE_NS::ClassId::Light) {
1146 sceneHolder->EnableLightComponent(entity);
1147 }
1148 if (classUid == SCENE_NS::ClassId::Environment) {
1149 sceneHolder->EnableEnvironmentComponent(entity);
1150 }
1151 }
1152 }
1153
1154 return entity;
1155 };
1156 if (GetValue(Asynchronous())) {
1157 AddEngineTask(MakeTask(
1158 [constructNode](const auto& sceneHolder) {
1159 constructNode(sceneHolder);
1160 return false;
1161 },
1162 sceneHolder_),
1163 false);
1164 } else {
1165 entity = constructNode(sceneHolder_);
1166 }
1167
1168 addToRootContainer = true;
1169 // If we don't have parent .. then attach to root.
1170 if (nodePath.empty() && !isResourceClassType) {
1171 nodePath = "/" + rootNodeId_ + "/";
1172 }
1173
1174 if (META_NS::Property<uint32_t> creationPolicy = nodeInterface->GetLifecycleInfo()) {
1175 creationPolicy->SetValue(NODE_LC_CREATED);
1176 nodeInterface->ClaimOwnershipOfEntity(true);
1177 }
1178 } else {
1179 // We expect to find the node from ecs (sooner or later)
1180 // If we are running synchronously, we could check it and even
1181 // tell the calling code if the node is there.
1182
1183 // We'd need to know some additional info about the node parent etc
1184 if (META_NS::Property<uint32_t> creationPolicy = nodeInterface->GetLifecycleInfo()) {
1185 creationPolicy->SetValue(NODE_LC_MIRROR_EXISTING);
1186 }
1187 }
1188
1189 SCENE_NS::INode::Ptr parent;
1190
1191 if (!isResourceClassType) {
1192 auto containable = interface_cast<META_NS::IContainable>(node);
1193 if (containable->GetParent()) {
1194 parent = interface_pointer_cast<SCENE_NS::INode>(containable->GetParent());
1195 }
1196
1197 if (!parent) {
1198 parent = addToRootContainer ? RootNode()->GetValue() : currentParent_;
1199 }
1200 }
1201
1202 nodeInterface->Initialize(ecsScene, ecsObject, parent, nodePath, nodeName, sceneHolder_, entity);
1203 }
1204
UpdateCachedNodePath(const SCENE_NS::INode::Ptr & node)1205 void UpdateCachedNodePath(const SCENE_NS::INode::Ptr& node) override
1206 {
1207 if (node) {
1208 if (interface_cast<META_NS::IObject>(node)->GetClassId() != SCENE_NS::ClassId::Node) {
1209 UpdateCachedReference(node);
1210 return;
1211 }
1212
1213 bool found = false;
1214
1215 for (auto&& ite : nodes_) {
1216 if (ite.second == node) {
1217 nodes_.erase(ite.first);
1218 break;
1219 }
1220 }
1221
1222 BASE_NS::string pathString = node->Path()->GetValue();
1223 pathString.append(node->Name()->GetValue());
1224 nodes_[pathString] = node;
1225 }
1226 }
1227
SetCacheEnabled(const SCENE_NS::INode::Ptr & node,bool enabled)1228 void SetCacheEnabled(const SCENE_NS::INode::Ptr& node, bool enabled) override
1229 {
1230 if (!node) {
1231 return;
1232 }
1233
1234 if (enabled) {
1235 BASE_NS::string pathString = node->Path()->GetValue();
1236 pathString.append(node->Name()->GetValue());
1237 nodes_[pathString] = node;
1238 } else {
1239 for (auto&& ite : nodes_) {
1240 if (ite.second == node) {
1241 nodes_.erase(ite.first);
1242 break;
1243 }
1244 }
1245 }
1246 }
1247
1248 typedef BASE_NS::shared_ptr<CORE_NS::IInterface> (*fun)(SceneImpl* me, const BASE_NS::string_view);
1249
relmat(SceneImpl * me,const BASE_NS::string_view p)1250 static BASE_NS::shared_ptr<CORE_NS::IInterface> relmat(SceneImpl* me, const BASE_NS::string_view p)
1251 {
1252 return me->ReleaseMaterial(p);
1253 }
relmesh(SceneImpl * me,const BASE_NS::string_view p)1254 static BASE_NS::shared_ptr<CORE_NS::IInterface> relmesh(SceneImpl* me, const BASE_NS::string_view p)
1255 {
1256 return me->ReleaseMesh(p);
1257 }
relanim(SceneImpl * me,const BASE_NS::string_view p)1258 static BASE_NS::shared_ptr<CORE_NS::IInterface> relanim(SceneImpl* me, const BASE_NS::string_view p)
1259 {
1260 return me->ReleaseAnimation(p);
1261 }
relnode(SceneImpl * me,const BASE_NS::string_view p)1262 static BASE_NS::shared_ptr<CORE_NS::IInterface> relnode(SceneImpl* me, const BASE_NS::string_view p)
1263 {
1264 return me->ReleaseNode(p);
1265 }
1266 template<typename Type>
CacheNode(const char * const tname,const SCENE_NS::INode::Ptr & node,BASE_NS::unordered_map<BASE_NS::string,typename Type::WeakPtr> & cache,fun func)1267 bool CacheNode(const char* const tname, const SCENE_NS::INode::Ptr& node,
1268 BASE_NS::unordered_map<BASE_NS::string, typename Type::WeakPtr>& cache, fun func)
1269 {
1270 if (auto typed = interface_pointer_cast<Type>(node)) {
1271 BASE_NS::string uri;
1272 bool found = false;
1273
1274 if (node->GetInterface(SCENE_NS::ICamera::UID)) {
1275 // handle camera naming..
1276 uri = node->Path()->GetValue();
1277 uri.append(node->Name()->GetValue());
1278
1279 } else {
1280 auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(typed);
1281 uri = sceneHolder_->GetResourceId(ecsObject->GetEntity());
1282 if (uri.empty()) {
1283 uri = node->Name()->GetValue() + ":" + BASE_NS::to_hex(ecsObject->GetEntity().id);
1284 }
1285 }
1286
1287 for (auto&& ite : cache) {
1288 if (ite.second.lock() == typed) {
1289 if (uri != ite.first) {
1290 cache[uri] = interface_pointer_cast<Type>(func(this, ite.first));
1291 SCENE_PLUGIN_VERBOSE_LOG("Updating cached reference of %s: %s", tname, uri.c_str());
1292 }
1293 found = true;
1294 break; // reference is valid so return true regardless if the node was moved
1295 }
1296 }
1297
1298 if (!found) {
1299 SCENE_PLUGIN_VERBOSE_LOG("Caching reference of %s: %s", tname, uri.c_str());
1300 cache[uri] = typed;
1301 }
1302 return true;
1303 }
1304 return false;
1305 }
UpdateCachedReference(const SCENE_NS::INode::Ptr & node)1306 void UpdateCachedReference(const SCENE_NS::INode::Ptr& node) override
1307 {
1308 if (node) {
1309 if (CacheNode<SCENE_NS::IMaterial>("material", node, materials_, relmat) ||
1310 CacheNode<SCENE_NS::IMesh>("mesh", node, meshes_, relmesh) ||
1311 CacheNode<META_NS::IAnimation>("animation", node, animations_, relanim)) {
1312 // completed. (meshes, materials and animations are not added to node list)
1313 return;
1314 }
1315 if (CacheNode<SCENE_NS::ICamera>("camera", node, cameras_, relnode)) {
1316 // camera cache updated.
1317 auto* obj = interface_cast<SCENE_NS::IEcsObject>(node);
1318 if (obj) {
1319 auto entity = obj->GetEntity();
1320 if (CORE_NS::EntityUtil::IsValid(entity)) {
1321 sceneHolder_->AddCamera(entity);
1322 } else {
1323 CORE_LOG_V("camera has no entity id yet");
1324 }
1325 } else {
1326 CORE_LOG_V("camera has no IEcsObject");
1327 }
1328 }
1329 // update node cache.
1330
1331 bool found = false;
1332 BASE_NS::string uri = node->Path()->GetValue();
1333 uri.append(node->Name()->GetValue());
1334
1335 for (auto&& ite : nodes_) {
1336 if (ite.second == node) {
1337 if (uri != ite.first) {
1338 nodes_[uri] = ReleaseNode(ite.first);
1339 }
1340 found = true;
1341 break; // reference is valid so return true regardless if the node was moved
1342 }
1343 }
1344
1345 if (!found) {
1346 nodes_[uri] = node;
1347 }
1348 }
1349 }
1350
1351 // Release Node Reference from cache
ReleaseNode(const BASE_NS::string_view name)1352 SCENE_NS::INode::Ptr ReleaseNode(const BASE_NS::string_view name) override
1353 {
1354 SCENE_NS::INode::Ptr ret {};
1355 if (auto ite = nodes_.extract(BASE_NS::string(name)); !ite.empty()) {
1356 ret = BASE_NS::move(ite.mapped());
1357 if (auto privateIntf = interface_cast<INodeEcsInterfacePrivate>(ret)) {
1358 privateIntf->ClaimOwnershipOfEntity(true);
1359 }
1360 RemoveNodeFromCurrentContainer(ret);
1361 if (auto cam = interface_pointer_cast<SCENE_NS::ICamera>(ret)) {
1362 ReleaseCamera(name);
1363 }
1364 }
1365 return ret;
1366 }
ReleaseNode(const SCENE_NS::INode::Ptr & node)1367 void ReleaseNode(const SCENE_NS::INode::Ptr& node) override
1368 {
1369 if (node) {
1370 ReleaseNode(node->Path()->GetValue() + node->Name()->GetValue());
1371 }
1372 }
1373
ReleaseMaterial(const BASE_NS::string_view name)1374 SCENE_NS::IMaterial::Ptr ReleaseMaterial(const BASE_NS::string_view name) override
1375 {
1376 SCENE_NS::IMaterial::Ptr ret {};
1377 if (auto ite = materials_.find(name); ite != materials_.end()) {
1378 ret = ite->second.lock();
1379 materials_.erase(ite);
1380 }
1381 return ret;
1382 }
1383
ReleaseMesh(const BASE_NS::string_view name)1384 SCENE_NS::IMesh::Ptr ReleaseMesh(const BASE_NS::string_view name) override
1385 {
1386 SCENE_NS::IMesh::Ptr ret {};
1387 if (auto ite = meshes_.find(name); ite != meshes_.end()) {
1388 ret = ite->second.lock();
1389 meshes_.erase(ite);
1390 }
1391
1392 return ret;
1393 }
1394
ReleaseAnimation(const BASE_NS::string_view name)1395 META_NS::IAnimation::Ptr ReleaseAnimation(const BASE_NS::string_view name) override
1396 {
1397 META_NS::IAnimation::Ptr ret {};
1398 if (auto ite = animations_.find(name); ite != animations_.end()) {
1399 ret = ite->second.lock();
1400 animations_.erase(ite);
1401 }
1402 return ret;
1403 }
1404
ReleaseCamera(const BASE_NS::string_view name)1405 SCENE_NS::ICamera::Ptr ReleaseCamera(const BASE_NS::string_view name)
1406 {
1407 SCENE_NS::ICamera::Ptr ret {};
1408 if (auto ite = cameras_.find(name); ite != cameras_.end()) {
1409 ret = ite->second.lock();
1410 // make sure the rendertarget/bitmap is also released.
1411 SetBitmap(nullptr, ret);
1412 cameras_.erase(ite);
1413 }
1414 return ret;
1415 }
1416
CreateMeshFromArraysI16(const BASE_NS::string_view name,SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays)1417 SCENE_NS::IMesh::Ptr CreateMeshFromArraysI16(
1418 const BASE_NS::string_view name, SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays) override
1419 {
1420 return CreateMeshFromArrays<uint16_t>(name, arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT16);
1421 }
1422
CreateMeshFromArraysI32(const BASE_NS::string_view name,SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays)1423 SCENE_NS::IMesh::Ptr CreateMeshFromArraysI32(
1424 const BASE_NS::string_view name, SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays) override
1425 {
1426 return CreateMeshFromArrays<uint32_t>(name, arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT32);
1427 }
1428
1429 template<typename IndicesType>
CreateMeshFromArrays(const BASE_NS::string_view name,SCENE_NS::MeshGeometryArrayPtr<IndicesType> arrays,RENDER_NS::IndexType indexType)1430 SCENE_NS::IMesh::Ptr CreateMeshFromArrays(const BASE_NS::string_view name,
1431 SCENE_NS::MeshGeometryArrayPtr<IndicesType> arrays, RENDER_NS::IndexType indexType)
1432 {
1433 auto nameString = BASE_NS::shared_ptr(new BASE_NS::string(name.data(), name.size()));
1434
1435 AddEngineTask(MakeTask(
1436 [arrays, nameString, indexType](auto sceneHolder) {
1437 auto meshEntity = sceneHolder->template CreateMeshFromArrays<IndicesType>(
1438 *nameString, arrays, indexType);
1439 if (!sceneHolder->IsAsync()) {
1440 *nameString = sceneHolder->GetResourceId(meshEntity);
1441 }
1442
1443 return false;
1444 },
1445 sceneHolder_),
1446 false);
1447 return GetMesh(*nameString);
1448 }
1449
InstantiateMaterialProxies()1450 void InstantiateMaterialProxies() override
1451 {
1452 AddEngineTask(MakeTask(
1453 [me = BASE_NS::weak_ptr(GetSelf<SCENE_NS::IScene>())](auto sceneHolder) {
1454 auto ids = sceneHolder->ListMaterialNames();
1455 sceneHolder->QueueApplicationTask(MakeTask(
1456 [ids](auto self) {
1457 for (auto& id : *ids) {
1458 self->GetMaterial(id);
1459 }
1460 return false;
1461 },
1462 me),
1463 false);
1464 return false;
1465 },
1466 sceneHolder_),
1467 false);
1468 }
1469
InstantiateMeshProxies()1470 void InstantiateMeshProxies() override
1471 {
1472 AddEngineTask(MakeTask(
1473 [me = BASE_NS::weak_ptr(GetSelf<SCENE_NS::IScene>())](auto sceneHolder) {
1474 auto ids = sceneHolder->ListMeshNames();
1475 sceneHolder->QueueApplicationTask(MakeTask(
1476 [ids](auto self) {
1477 for (auto& id : *ids) {
1478 self->GetMesh(id);
1479 }
1480
1481 return false;
1482 },
1483 me),
1484 false);
1485
1486 return false;
1487 },
1488 sceneHolder_),
1489 false);
1490 }
1491
1492 public:
~SceneImpl()1493 ~SceneImpl() override
1494 {
1495 allAnims_.clear();
1496 if (sceneHolder_) {
1497 DetachScene();
1498 }
1499
1500 cameraNodePtr_.reset();
1501 rootNodePtr_.reset();
1502
1503 UnsubscribeFromPropertyChanges();
1504
1505 if (sceneHolder_) {
1506 sceneHolder_->Uninitialize();
1507 sceneHolder_.reset();
1508 }
1509 }
1510
SetEcsInitializationCallback(IPrepareSceneForInitialization::WeakPtr callback)1511 void SetEcsInitializationCallback(IPrepareSceneForInitialization::WeakPtr callback) override
1512 {
1513 if (sceneHolder_) {
1514 sceneHolder_->SetEcsInitializationCallback(callback);
1515 } else {
1516 CORE_LOG_W("%s: sceneholder does not exist, cannot set callback", __func__);
1517 }
1518 }
1519
GetWorldAABB(const BASE_NS::Math::Mat4X4 & world,const BASE_NS::Math::Vec3 & aabbMin,const BASE_NS::Math::Vec3 & aabbMax)1520 SCENE_NS::IPickingResult::Ptr GetWorldAABB(const BASE_NS::Math::Mat4X4& world, const BASE_NS::Math::Vec3& aabbMin,
1521 const BASE_NS::Math::Vec3& aabbMax) override
1522 {
1523 auto ret = META_NS::GetObjectRegistry().Create<SCENE_NS::IPickingResult>(SCENE_NS::ClassId::PendingVec3Request);
1524 if (ret) {
1525 AddEngineTask(
1526 META_NS::MakeCallback<META_NS::ITaskQueueTask>([w = world, min = aabbMin, max = aabbMax,
1527 weakRet = BASE_NS::weak_ptr(ret),
1528 weakSh = BASE_NS::weak_ptr(sceneHolder_)]() {
1529 if (auto sh = weakSh.lock()) {
1530 if (auto ret = weakRet.lock()) {
1531 if (sh->GetWorldAABB(ret, w, min, max)) {
1532 sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet]() {
1533 if (auto writable =
1534 interface_pointer_cast<SCENE_NS::IPendingRequestData<BASE_NS::Math::Vec3>>(
1535 weakRet)) {
1536 writable->MarkReady();
1537 }
1538 return false;
1539 }),
1540 false);
1541 }
1542 }
1543 }
1544 return false;
1545 }),
1546 false);
1547 return ret;
1548 }
1549 return SCENE_NS::IPickingResult::Ptr();
1550 }
1551
RayCast(const BASE_NS::Math::Vec3 & start,const BASE_NS::Math::Vec3 & direction)1552 SCENE_NS::IRayCastResult::Ptr RayCast(
1553 const BASE_NS::Math::Vec3& start, const BASE_NS::Math::Vec3& direction) override
1554 {
1555 auto ret =
1556 META_NS::GetObjectRegistry().Create<SCENE_NS::IRayCastResult>(SCENE_NS::ClassId::PendingDistanceRequest);
1557 if (ret) {
1558 AddEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([origin = start, dir = direction,
1559 weakRet = BASE_NS::weak_ptr(ret),
1560 weakSh = BASE_NS::weak_ptr(sceneHolder_),
1561 weakSelf = BASE_NS::weak_ptr(
1562 GetSelf<SCENE_NS::IScene>())]() {
1563 if (auto sh = weakSh.lock()) {
1564 if (auto ret = weakRet.lock()) {
1565 if (sh->RayCast(ret, origin, dir)) {
1566 sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet,
1567 weakSelf]() {
1568 if (auto writable =
1569 interface_pointer_cast<SCENE_NS::IPendingRequestData<SCENE_NS::NodeDistance>>(
1570 weakRet)) {
1571 if (auto self = weakSelf.lock()) {
1572 // resolve proxy nodes
1573 for (size_t ii = writable->MetaData().size(); ii > 0;) {
1574 --ii;
1575 writable->MutableData().at(ii).node =
1576 self->GetNode(writable->MetaData().at(ii));
1577 }
1578 writable->MarkReady();
1579 }
1580 }
1581 return false;
1582 }),
1583 false);
1584 }
1585 }
1586 }
1587 return false;
1588 }),
1589 false);
1590 return ret;
1591 }
1592 return SCENE_NS::IRayCastResult::Ptr();
1593 }
1594
RayCast(const BASE_NS::Math::Vec3 & start,const BASE_NS::Math::Vec3 & direction,uint64_t layerMask)1595 SCENE_NS::IRayCastResult::Ptr RayCast(
1596 const BASE_NS::Math::Vec3& start, const BASE_NS::Math::Vec3& direction, uint64_t layerMask) override
1597 {
1598 auto ret =
1599 META_NS::GetObjectRegistry().Create<SCENE_NS::IRayCastResult>(SCENE_NS::ClassId::PendingDistanceRequest);
1600 if (ret) {
1601 AddEngineTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([origin = start, dir = direction, layerMask,
1602 weakRet = BASE_NS::weak_ptr(ret),
1603 weakSh = BASE_NS::weak_ptr(sceneHolder_),
1604 weakSelf = BASE_NS::weak_ptr(
1605 GetSelf<SCENE_NS::IScene>())]() {
1606 if (auto sh = weakSh.lock()) {
1607 if (auto ret = weakRet.lock()) {
1608 if (sh->RayCast(ret, origin, dir, layerMask)) {
1609 sh->QueueApplicationTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>([weakRet,
1610 weakSelf]() {
1611 if (auto writable =
1612 interface_pointer_cast<SCENE_NS::IPendingRequestData<SCENE_NS::NodeDistance>>(
1613 weakRet)) {
1614 if (auto self = weakSelf.lock()) {
1615 // resolve proxy nodes
1616 for (size_t ii = writable->MetaData().size(); ii > 0;) {
1617 --ii;
1618 writable->MutableData().at(ii).node =
1619 self->GetNode(writable->MetaData().at(ii));
1620 }
1621 writable->MarkReady();
1622 }
1623 }
1624 return false;
1625 }),
1626 false);
1627 }
1628 }
1629 }
1630 return false;
1631 }),
1632 false);
1633 return ret;
1634 }
1635 return SCENE_NS::IRayCastResult::Ptr();
1636 }
RenderCameras()1637 BASE_NS::vector<CORE_NS::Entity> RenderCameras() override
1638 {
1639 if (sceneHolder_) {
1640 return sceneHolder_->RenderCameras();
1641 }
1642 return {};
1643 }
ActivateCamera(const SCENE_NS::ICamera::Ptr & camera)1644 void ActivateCamera(const SCENE_NS::ICamera::Ptr& camera) override
1645 {
1646 if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
1647 auto ent = e->GetEntity();
1648 sceneHolder_->ActivateCamera(ent);
1649 }
1650 }
1651
DeactivateCamera(const SCENE_NS::ICamera::Ptr & camera)1652 void DeactivateCamera(const SCENE_NS::ICamera::Ptr& camera) override
1653 {
1654 if (!camera) {
1655 return;
1656 }
1657
1658 if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
1659 sceneHolder_->DeactivateCamera(e->GetEntity());
1660 }
1661 }
IsCameraActive(const SCENE_NS::ICamera::Ptr & camera)1662 bool IsCameraActive(const SCENE_NS::ICamera::Ptr& camera) override
1663 {
1664 if (!camera) {
1665 return false;
1666 }
1667
1668 if (auto e = interface_pointer_cast<SCENE_NS::IEcsObject>(camera)) {
1669 return sceneHolder_->IsCameraActive(e->GetEntity());
1670 }
1671 return false;
1672 }
1673
1674 private:
SubscribeToPropertyChanges()1675 void SubscribeToPropertyChanges()
1676 {
1677 if (RootNode()) {
1678 rootNodeChangedToken_ = RootNode()->OnChanged()->AddHandler(
1679 META_NS::MakeCallback<META_NS::IOnChanged>(this, &SceneImpl::OnRootNodeChanged));
1680 }
1681
1682 if (Uri()) {
1683 uriHandlerToken_ = Uri()->OnChanged()->AddHandler(
1684 META_NS::MakeCallback<META_NS::IOnChanged>([this]() { this->Load(Uri()->GetValue()); }));
1685 }
1686
1687 // Start listening changes of the scene controller properties. These may go to different place some day
1688 if (SystemGraphUri()) {
1689 systemGraphUriHandlerToken_ = SystemGraphUri()->OnChanged()->AddHandler(
1690 META_NS::MakeCallback<META_NS::IOnChanged>(this, &SceneImpl::onSystemGraphUriChanged));
1691 }
1692 }
1693
UnsubscribeFromPropertyChanges()1694 void UnsubscribeFromPropertyChanges()
1695 {
1696 if (Uri()) {
1697 Uri()->OnChanged()->RemoveHandler(uriHandlerToken_);
1698 uriHandlerToken_ = {};
1699 }
1700
1701 if (RootNode()) {
1702 RootNode()->OnChanged()->RemoveHandler(rootNodeChangedToken_);
1703 rootNodeChangedToken_ = {};
1704 }
1705
1706 if (SystemGraphUri()) {
1707 SystemGraphUri()->OnChanged()->RemoveHandler(systemGraphUriHandlerToken_);
1708 systemGraphUriHandlerToken_ = {};
1709 }
1710
1711 if (Asynchronous()) {
1712 Asynchronous()->OnChanged()->RemoveHandler(asyncChangedToken_);
1713 asyncChangedToken_ = {};
1714 }
1715 }
1716
OnRootNodeChanged()1717 void OnRootNodeChanged()
1718 {
1719 auto contentObject = interface_pointer_cast<META_NS::IObject>(RootNode()->GetValue());
1720 contentImpl_->SetContent(contentObject);
1721
1722 META_NS::HierarchyChangeModeValue changeMode;
1723 changeMode.Set(META_NS::HierarchyChangeMode::NOTIFY_CONTAINER);
1724 hierarchyController_->SetTarget(contentObject, changeMode);
1725 }
1726
1727 META_NS::IEvent::Token systemGraphUriHandlerToken_ {};
1728 META_NS::IEvent::Token renderSizeHandlerToken_ {};
1729 META_NS::IEvent::Token cameraHandlerToken_ {};
1730 META_NS::IEvent::Token uriHandlerToken_ {};
1731 META_NS::IEvent::Token rootNodeChangedToken_ {};
1732 META_NS::IEvent::Token renderModeChangedToken_ {};
1733 META_NS::IEvent::Token asyncChangedToken_ {};
1734
1735 SceneHolder::Ptr sceneHolder_;
1736
1737 BASE_NS::string rootNodeId_;
1738 BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::INode::Ptr> nodes_;
1739 BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::ICamera::WeakPtr> cameras_;
1740 BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMesh::WeakPtr> meshes_;
1741 BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMaterial::WeakPtr> materials_;
1742 BASE_NS::unordered_map<BASE_NS::string, META_NS::IAnimation::WeakPtr> animations_;
1743
1744 BASE_NS::unordered_map<BASE_NS::string, SCENE_NS::IMaterial::WeakPtr> uriMaterials_;
1745
1746 uint64_t instanceNumber_ { 0 };
1747
1748 // Preserve property instances while scene / ecs is invalid
1749 SCENE_NS::INode::Ptr rootNodePtr_ {};
1750 SCENE_NS::ICamera::Ptr cameraNodePtr_ {};
1751 META_NS::IContent::Ptr contentImpl_ {};
1752
1753 uint64_t defaultCameraHandle_ { 0 };
1754
1755 // We need to add a node onto a container in bit awkward position (without exposing it to a public api)
1756 // Store it here.
1757 SCENE_NS::INode::Ptr currentParent_ {};
1758 META_NS::IObjectHierarchyObserver::Ptr hierarchyController_;
1759
1760 META_NS::IAnimationController::Ptr animationController_;
1761 };
1762
GetAnimations() const1763 BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> SceneImpl::GetAnimations() const
1764 {
1765 return animationController_->GetAnimations();
1766 }
GetRunning() const1767 BASE_NS::vector<BASE_NS::weak_ptr<META_NS::IAnimation>> SceneImpl::GetRunning() const
1768 {
1769 return animationController_->GetRunning();
1770 }
AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation> & animation)1771 bool SceneImpl::AddAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation)
1772 {
1773 return animationController_->AddAnimation(animation);
1774 }
RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation> & animation)1775 bool SceneImpl::RemoveAnimation(const BASE_NS::shared_ptr<META_NS::IAnimation>& animation)
1776 {
1777 return animationController_->RemoveAnimation(animation);
1778 }
Clear()1779 void SceneImpl::Clear()
1780 {
1781 animationController_->Clear();
1782 }
Step(const META_NS::IClock::ConstPtr & clock)1783 META_NS::IAnimationController::StepInfo SceneImpl::Step(const META_NS::IClock::ConstPtr& clock)
1784 {
1785 return animationController_->Step(clock);
1786 }
1787
1788 } // namespace
SCENE_BEGIN_NAMESPACE()1789 SCENE_BEGIN_NAMESPACE()
1790 void RegisterSceneImpl()
1791 {
1792 META_NS::GetObjectRegistry().RegisterObjectType<SceneImpl>();
1793 }
UnregisterSceneImpl()1794 void UnregisterSceneImpl()
1795 {
1796 META_NS::GetObjectRegistry().UnregisterObjectType<SceneImpl>();
1797 }
1798 SCENE_END_NAMESPACE()
1799