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 "NodeImpl.h"
16
17 #include <meta/api/make_callback.h>
18 #include <meta/interface/intf_containable.h>
19 #include <meta/interface/intf_container.h>
20 #include <meta/interface/intf_task_queue.h>
21 #include <meta/interface/intf_task_queue_registry.h>
22 #include <napi_api.h>
23 #include <scene_plugin/api/camera_uid.h>
24 #include <scene_plugin/api/light_uid.h>
25 #include <scene_plugin/api/node_uid.h>
26 #include <scene_plugin/api/scene.h>
27 #include <scene_plugin/interface/intf_node.h>
28
29 #include "BaseObjectJS.h"
RegisterEnums(NapiApi::Object exports)30 void NodeImpl::RegisterEnums(NapiApi::Object exports)
31 {
32 napi_value v;
33 NapiApi::Object NodeType(exports.GetEnv());
34 #define DECL_ENUM(enu, x) \
35 { \
36 napi_create_uint32(enu.GetEnv(), NodeImpl::NodeType::x, &v); \
37 enu.Set(#x, v); \
38 }
39 DECL_ENUM(NodeType, NODE);
40 DECL_ENUM(NodeType, GEOMETRY);
41 DECL_ENUM(NodeType, CAMERA);
42 DECL_ENUM(NodeType, LIGHT);
43 #undef DECL_ENUM
44 exports.Set("NodeType", NodeType);
45 }
NodeImpl(NodeType type)46 NodeImpl::NodeImpl(NodeType type) : SceneResourceImpl(SceneResourceImpl::NODE), type_(type)
47 {
48 LOG_F("NodeImpl ++");
49 }
~NodeImpl()50 NodeImpl::~NodeImpl()
51 {
52 LOG_F("NodeImpl --");
53 }
54
GetInstanceImpl(uint32_t id)55 void* NodeImpl::GetInstanceImpl(uint32_t id)
56 {
57 if (id == NodeImpl::ID) {
58 return this;
59 }
60 return SceneResourceImpl::GetInstanceImpl(id);
61 }
62
GetPropertyDescs(BASE_NS::vector<napi_property_descriptor> & props)63 void NodeImpl::GetPropertyDescs(BASE_NS::vector<napi_property_descriptor>& props)
64 {
65 using namespace NapiApi;
66 // META_NS::IContainer;
67
68 SceneResourceImpl::GetPropertyDescs(props);
69
70 // node
71
72 props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetPath>("path"));
73 props.push_back(TROGetSetProperty<Object, NodeImpl, &NodeImpl::GetPosition, &NodeImpl::SetPosition>("position"));
74 props.push_back(TROGetSetProperty<Object, NodeImpl, &NodeImpl::GetRotation, &NodeImpl::SetRotation>("rotation"));
75 props.push_back(TROGetSetProperty<Object, NodeImpl, &NodeImpl::GetScale, &NodeImpl::SetScale>("scale"));
76 props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetParent>("parent"));
77 props.push_back(TROGetSetProperty<bool, NodeImpl, &NodeImpl::GetVisible, &NodeImpl::SetVisible>("visible"));
78 props.push_back(TROGetSetProperty<bool, NodeImpl, &NodeImpl::GetChildContainer, &NodeImpl::SetVisible>("children"));
79 props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetNodeType>("nodeType"));
80 props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetLayerMask>("layerMask"));
81
82 // node methods
83 props.push_back(MakeTROMethod<FunctionContext<>, NodeImpl, &NodeImpl::Dispose>("destroy"));
84 props.push_back(
85 MakeTROMethod<FunctionContext<BASE_NS::string>, NodeImpl, &NodeImpl::GetNodeByPath>("getNodeByPath"));
86
87 // layermask methods.
88 props.push_back(MakeTROMethod<FunctionContext<uint32_t>, NodeImpl, &NodeImpl::GetLayerMaskEnabled>("getEnabled"));
89 props.push_back(
90 MakeTROMethod<FunctionContext<uint32_t, bool>, NodeImpl, &NodeImpl::SetLayerMaskEnabled>("setEnabled"));
91
92 // container
93 props.push_back(MakeTROMethod<FunctionContext<Object>, NodeImpl, &NodeImpl::AppendChild>("append"));
94 props.push_back(
95 MakeTROMethod<FunctionContext<Object, Object>, NodeImpl, &NodeImpl::InsertChildAfter>("insertAfter"));
96 props.push_back(MakeTROMethod<FunctionContext<Object>, NodeImpl, &NodeImpl::RemoveChild>("remove"));
97 props.push_back(MakeTROMethod<FunctionContext<uint32_t>, NodeImpl, &NodeImpl::GetChild>("get"));
98 props.push_back(MakeTROMethod<FunctionContext<>, NodeImpl, &NodeImpl::ClearChildren>("clear"));
99 props.push_back(MakeTROMethod<FunctionContext<>, NodeImpl, &NodeImpl::GetCount>("count"));
100 }
Dispose(NapiApi::FunctionContext<> & ctx)101 napi_value NodeImpl::Dispose(NapiApi::FunctionContext<>& ctx)
102 {
103 // Dispose of the native object. (makes the js object invalid)
104 posProxy_.reset();
105 sclProxy_.reset();
106 rotProxy_.reset();
107 if (TrueRootObject* instance = GetThisRootObject(ctx)) {
108 instance->DisposeNative();
109 }
110 scene_.Reset();
111 return ctx.GetUndefined();
112 }
GetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t> & ctx)113 napi_value NodeImpl::GetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t>& ctx)
114 {
115 uint32_t bit = ctx.Arg<0>();
116 bool enabled = false;
117 if (auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx))) {
118 uint64_t mask = 1ull << bit;
119 ExecSyncTask([node, mask, &enabled]() {
120 enabled = node->LayerMask()->GetValue() & mask;
121 return META_NS::IAny::Ptr {};
122 });
123 }
124 return ctx.GetBoolean(enabled);
125 }
SetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t,bool> & ctx)126 napi_value NodeImpl::SetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t, bool>& ctx)
127 {
128 uint32_t bit = ctx.Arg<0>();
129 bool enabled = ctx.Arg<1>();
130 if (auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx))) {
131 uint64_t mask = 1ull << bit;
132 ExecSyncTask([node, enabled, mask]() {
133 if (enabled) {
134 node->LayerMask()->SetValue(node->LayerMask()->GetValue() | mask);
135 } else {
136 node->LayerMask()->SetValue(node->LayerMask()->GetValue() & ~mask);
137 }
138 return META_NS::IAny::Ptr {};
139 });
140 }
141 return ctx.GetUndefined();
142 }
143
GetNodeType(NapiApi::FunctionContext<> & ctx)144 napi_value NodeImpl::GetNodeType(NapiApi::FunctionContext<>& ctx)
145 {
146 uint32_t type = -1; // return -1 if the object does not exist anymore
147 if (auto node = interface_cast<SCENE_NS::INode>(GetThisNativeObject(ctx))) {
148 type = type_;
149 }
150 napi_value value;
151 napi_status status = napi_create_uint32(ctx, type, &value);
152 return value;
153 }
154
GetLayerMask(NapiApi::FunctionContext<> & ctx)155 napi_value NodeImpl::GetLayerMask(NapiApi::FunctionContext<>& ctx)
156 {
157 if (auto node = interface_cast<SCENE_NS::INode>(GetThisNativeObject(ctx))) {
158 return ctx.This();
159 }
160 return ctx.GetUndefined();
161 }
162
GetPath(NapiApi::FunctionContext<> & ctx)163 napi_value NodeImpl::GetPath(NapiApi::FunctionContext<>& ctx)
164 {
165 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
166 BASE_NS::string path;
167 if (node) {
168 ExecSyncTask([node, &path]() {
169 if (interface_cast<META_NS::IContainable>(node)->GetParent()) {
170 path = node->Path()->GetValue();
171 }
172 return META_NS::IAny::Ptr {};
173 });
174 }
175 napi_value value;
176 napi_status status = napi_create_string_utf8(ctx, path.c_str(), path.length(), &value);
177 return value;
178 }
179
GetVisible(NapiApi::FunctionContext<> & ctx)180 napi_value NodeImpl::GetVisible(NapiApi::FunctionContext<>& ctx)
181 {
182 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
183 bool visible = false;
184 if (node) {
185 ExecSyncTask([node, &visible]() {
186 visible = node->Visible()->GetValue();
187 return META_NS::IAny::Ptr {};
188 });
189 }
190 napi_value value;
191 napi_status status = napi_get_boolean(ctx, visible, &value);
192 return value;
193 }
SetVisible(NapiApi::FunctionContext<bool> & ctx)194 void NodeImpl::SetVisible(NapiApi::FunctionContext<bool>& ctx)
195 {
196 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
197 if (node) {
198 bool visible = ctx.Arg<0>();
199 ExecSyncTask([node, visible]() {
200 node->Visible()->SetValue(visible);
201 return META_NS::IAny::Ptr {};
202 });
203 }
204 }
205
GetPosition(NapiApi::FunctionContext<> & ctx)206 napi_value NodeImpl::GetPosition(NapiApi::FunctionContext<>& ctx)
207 {
208 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
209 if (!node) {
210 return ctx.GetUndefined();
211 }
212 if (posProxy_ == nullptr) {
213 posProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx, node->Position());
214 }
215 return posProxy_ ? posProxy_->Value() : NapiApi::Value<NapiApi::Object>();
216 }
217
SetPosition(NapiApi::FunctionContext<NapiApi::Object> & ctx)218 void NodeImpl::SetPosition(NapiApi::FunctionContext<NapiApi::Object>& ctx)
219 {
220 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
221 if (!node) {
222 return;
223 }
224 NapiApi::Object obj = ctx.Arg<0>();
225 if (posProxy_ == nullptr) {
226 posProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx, node->Position());
227 }
228 posProxy_->SetValue(obj);
229 }
230
GetScale(NapiApi::FunctionContext<> & ctx)231 napi_value NodeImpl::GetScale(NapiApi::FunctionContext<>& ctx)
232 {
233 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
234 if (!node) {
235 return ctx.GetUndefined();
236 }
237 if (sclProxy_ == nullptr) {
238 sclProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx, node->Scale());
239 }
240 return sclProxy_ ? sclProxy_->Value() : NapiApi::Value<NapiApi::Object>();
241 }
242
SetScale(NapiApi::FunctionContext<NapiApi::Object> & ctx)243 void NodeImpl::SetScale(NapiApi::FunctionContext<NapiApi::Object>& ctx)
244 {
245 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
246 if (!node) {
247 return;
248 }
249 NapiApi::Object obj = ctx.Arg<0>();
250 if (sclProxy_ == nullptr) {
251 sclProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx, node->Scale());
252 }
253 sclProxy_->SetValue(obj);
254 }
255
GetRotation(NapiApi::FunctionContext<> & ctx)256 napi_value NodeImpl::GetRotation(NapiApi::FunctionContext<>& ctx)
257 {
258 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
259 if (!node) {
260 return ctx.GetUndefined();
261 }
262 if (rotProxy_ == nullptr) {
263 rotProxy_ = BASE_NS::make_unique<QuatProxy>(ctx, node->Rotation());
264 }
265 return rotProxy_ ? rotProxy_->Value() : NapiApi::Value<NapiApi::Object>();
266 }
267
SetRotation(NapiApi::FunctionContext<NapiApi::Object> & ctx)268 void NodeImpl::SetRotation(NapiApi::FunctionContext<NapiApi::Object>& ctx)
269 {
270 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
271 if (!node) {
272 return;
273 }
274 NapiApi::Object obj = ctx.Arg<0>();
275 if (rotProxy_ == nullptr) {
276 rotProxy_ = BASE_NS::make_unique<QuatProxy>(ctx, node->Rotation());
277 }
278 rotProxy_->SetValue(obj);
279 }
280
GetParent(NapiApi::FunctionContext<> & ctx)281 napi_value NodeImpl::GetParent(NapiApi::FunctionContext<>& ctx)
282 {
283 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
284 if (!node) {
285 return ctx.GetNull();
286 }
287 META_NS::IObject::Ptr root;
288 if (auto containable = interface_cast<META_NS::IContainable>(node)) {
289 ExecSyncTask([containable, &root]() {
290 root = interface_pointer_cast<META_NS::IObject>(containable->GetParent());
291 return META_NS::IAny::Ptr {};
292 });
293 }
294
295 if (!root) {
296 // no parent.
297 return ctx.GetNull();
298 }
299
300 if (auto cached = FetchJsObj(root)) {
301 // always return the same js object.
302 return cached;
303 }
304 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
305 CORE_LOG_F("INVALID SCENE!");
306 }
307
308 // create new js object for the native node.
309 NapiApi::Object argJS(ctx);
310 napi_value args[] = { scene_.GetValue(), argJS };
311
312 return CreateFromNativeInstance(ctx, root, false /*these are owned by the scene*/, BASE_NS::countof(args), args);
313 }
314
GetChildContainer(NapiApi::FunctionContext<> & ctx)315 napi_value NodeImpl::GetChildContainer(NapiApi::FunctionContext<>& ctx)
316 {
317 // make sure the child list is up to date.
318 auto node = interface_pointer_cast<SCENE_NS::INode>(GetThisNativeObject(ctx));
319 if (node) {
320 ExecSyncTask([node]() {
321 node->BuildChildren(SCENE_NS::INode::BuildBehavior::NODE_BUILD_ONLY_DIRECT_CHILDREN);
322 return META_NS::IAny::Ptr {};
323 });
324 }
325
326 // Node implements Container<Node>
327 return ctx.This();
328 }
329
330 // container implementation
SkipNode(SCENE_NS::INode::Ptr node)331 bool SkipNode(SCENE_NS::INode::Ptr node)
332 {
333 auto o = interface_cast<META_NS::IObject>(node);
334 auto classid = o->GetClassId();
335 // white list of nodes that are actual nodes..
336 if ((classid == SCENE_NS::ClassId::Camera) || (classid == SCENE_NS::ClassId::Light) ||
337 (classid == SCENE_NS::ClassId::Node)) {
338 return false;
339 }
340 return true;
341 }
GetChild(NapiApi::FunctionContext<uint32_t> & ctx)342 napi_value NodeImpl::GetChild(NapiApi::FunctionContext<uint32_t>& ctx)
343 {
344 uint32_t index = ctx.Arg<0>();
345 META_NS::IObject::Ptr child;
346 auto metaobj = GetThisNativeObject(ctx);
347 if (auto container = interface_cast<META_NS::IContainer>(metaobj)) {
348 ExecSyncTask([container, index, &child]() {
349 auto data = container->GetAll<SCENE_NS::INode>();
350 int count = 0;
351 for (auto d : data) {
352 // Skip nodes that are not real "nodes"
353 if (SkipNode(d)) {
354 continue;
355 }
356 if (count == index) {
357 child = interface_pointer_cast<META_NS::IObject>(d);
358 break;
359 }
360 count++;
361 }
362 return META_NS::IAny::Ptr {};
363 });
364 }
365 if (!child) {
366 // return null
367 napi_value value;
368 napi_get_null(ctx, &value);
369 return value;
370 }
371 auto cached = FetchJsObj(child);
372 if (!cached) {
373 // was not cached yet, so recreate.
374 NapiApi::Object argJS(ctx);
375 auto scn = scene_.GetObject();
376 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
377 CORE_LOG_F("INVALID SCENE!");
378 }
379
380 napi_value args[] = { scene_.GetValue(), argJS };
381
382 cached =
383 CreateFromNativeInstance(ctx, child, false /*these are owned by the scene*/, BASE_NS::countof(args), args);
384 }
385 return cached;
386 }
387
GetCount(NapiApi::FunctionContext<> & ctx)388 napi_value NodeImpl::GetCount(NapiApi::FunctionContext<>& ctx)
389 {
390 uint32_t count = 0;
391 auto metaobj = GetThisNativeObject(ctx);
392 if (auto container = interface_cast<META_NS::IContainer>(metaobj)) {
393 ExecSyncTask([container, &count]() {
394 auto data = container->GetAll<SCENE_NS::INode>();
395 for (auto d : data) {
396 // Skip nodes that are not real "nodes"
397 if (SkipNode(d)) {
398 continue;
399 }
400 count++;
401 }
402 return META_NS::IAny::Ptr {};
403 });
404 }
405 napi_value value;
406 napi_status nstatus = napi_create_uint32(ctx, count, &value);
407 return value;
408 }
AppendChild(NapiApi::FunctionContext<NapiApi::Object> & ctx)409 napi_value NodeImpl::AppendChild(NapiApi::FunctionContext<NapiApi::Object>& ctx)
410 {
411 NapiApi::Object childJS = ctx.Arg<0>();
412 if ((napi_value)childJS == nullptr) {
413 // okay. Invalid arg error?
414 return ctx.GetUndefined();
415 }
416 auto childNode = GetNativeMeta<SCENE_NS::INode>(childJS);
417 if (!childNode) {
418 return ctx.GetUndefined();
419 }
420
421 auto metaobj = GetThisNativeObject(ctx);
422 if (auto container = interface_cast<META_NS::IContainer>(metaobj)) {
423 ExecSyncTask([container, childNode]() {
424 container->Add(childNode);
425 childNode->Visible()->SetValue(true);
426 return META_NS::IAny::Ptr {};
427 });
428 }
429
430 // make the js object keep a weak ref again (scene keeps the native object alive)
431 // (or move ownership back from SceneJS? and remove dispose hook?)
432 if (auto tro = GetRootObject(ctx, childJS)) {
433 if (auto native = tro->GetNativeObject()) {
434 tro->SetNativeObject(nullptr, true);
435 tro->SetNativeObject(native, false);
436 native.reset();
437 }
438 }
439
440 return ctx.GetUndefined();
441 }
InsertChildAfter(NapiApi::FunctionContext<NapiApi::Object,NapiApi::Object> & ctx)442 napi_value NodeImpl::InsertChildAfter(NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object>& ctx)
443 {
444 NapiApi::Object childJS = ctx.Arg<0>();
445 NapiApi::Object siblingJS = ctx.Arg<1>();
446
447 if ((napi_value)childJS == nullptr) {
448 // okay. Invalid arg error?
449 return ctx.GetUndefined();
450 }
451 auto childNode = GetNativeMeta<SCENE_NS::INode>(childJS);
452 if (!childNode) {
453 return ctx.GetUndefined();
454 }
455 SCENE_NS::INode::Ptr siblingNode;
456 if (siblingJS) {
457 siblingNode = GetNativeMeta<SCENE_NS::INode>(siblingJS);
458 }
459
460 auto metaobj = GetThisNativeObject(ctx);
461 if (auto container = interface_cast<META_NS::IContainer>(metaobj)) {
462 ExecSyncTask([container, childNode, siblingNode]() {
463 if (siblingNode) {
464 auto data = container->GetAll<SCENE_NS::INode>();
465 int64_t index = 0;
466 for (auto d : data) {
467 index++;
468 if (d == siblingNode) {
469 // insert "after" the hit
470 container->Insert(index, childNode);
471 childNode->Visible()->SetValue(true);
472 return META_NS::IAny::Ptr {};
473 }
474 }
475 // did not find the sibling.. do nothing? add first? add last?
476 // for now add last..
477 container->Add(childNode);
478 childNode->Visible()->SetValue(true);
479 return META_NS::IAny::Ptr {};
480 }
481 // insert as first..
482 container->Insert(0, childNode);
483 childNode->Visible()->SetValue(true);
484 return META_NS::IAny::Ptr {};
485 });
486 }
487
488 // make the js object keep a weak ref again (scene keeps the native object alive)
489 // (or move ownership back from SceneJS? and remove dispose hook?)
490 if (auto tro = GetRootObject(ctx, childJS)) {
491 if (auto native = tro->GetNativeObject()) {
492 tro->SetNativeObject(nullptr, true);
493 tro->SetNativeObject(native, false);
494 native.reset();
495 }
496 }
497
498 return ctx.GetUndefined();
499 }
500
ResetNativeObj(NapiApi::FunctionContext<> & ctx,NapiApi::Object & obj)501 void NodeImpl::ResetNativeObj(NapiApi::FunctionContext<>& ctx, NapiApi::Object& obj)
502 {
503 if (auto tro = GetRootObject(ctx, obj)) {
504 if (auto native = tro->GetNativeObject()) {
505 tro->SetNativeObject(nullptr, false);
506 tro->SetNativeObject(native, true);
507 native.reset();
508 }
509 }
510 }
511
RemoveChild(NapiApi::FunctionContext<NapiApi::Object> & ctx)512 napi_value NodeImpl::RemoveChild(NapiApi::FunctionContext<NapiApi::Object>& ctx)
513 {
514 // okay. just detach from parent. (make parent invalid)
515 // and disable it.
516 NapiApi::Object childJS = ctx.Arg<0>();
517 if ((napi_value)childJS == nullptr) {
518 // okay. Invalid arg error?
519 return ctx.GetUndefined();
520 }
521 auto childNode = GetNativeMeta<SCENE_NS::INode>(childJS);
522 if (!childNode) {
523 return ctx.GetUndefined();
524 }
525
526 // make the js object keep a strong ref.
527 // (or give SceneJS ownership? and add dispose hook?)
528 if (auto tro = GetRootObject(ctx, childJS)) {
529 if (auto native = tro->GetNativeObject()) {
530 tro->SetNativeObject(nullptr, false);
531 tro->SetNativeObject(native, true);
532 native.reset();
533 }
534 }
535
536 auto metaobj = GetThisNativeObject(ctx);
537 if (auto container = interface_cast<META_NS::IContainer>(metaobj)) {
538 ExecSyncTask([container, childNode]() {
539 container->Remove(childNode);
540 childNode->Visible()->SetValue(false);
541 return META_NS::IAny::Ptr {};
542 });
543 }
544 return ctx.GetUndefined();
545 }
546
ClearChildren(NapiApi::FunctionContext<> & ctx)547 napi_value NodeImpl::ClearChildren(NapiApi::FunctionContext<>& ctx)
548 {
549 auto metaobj = GetThisNativeObject(ctx);
550 BASE_NS::vector<SCENE_NS::INode::Ptr> removedNodes;
551 if (auto container = interface_cast<META_NS::IContainer>(metaobj)) {
552 ExecSyncTask([container, &removedNodes]() {
553 auto tmp = container->GetAll();
554 for (auto t : tmp) {
555 if (auto node = interface_pointer_cast<SCENE_NS::INode>(t)) {
556 if (SkipNode(node)) {
557 continue;
558 }
559 container->Remove(node);
560 node->Visible()->SetValue(false);
561 removedNodes.emplace_back(BASE_NS::move(node));
562 }
563 }
564 return META_NS::IAny::Ptr {};
565 });
566 for (auto node : removedNodes) {
567 if (auto cached = FetchJsObj(node)) {
568 ResetNativeObj(ctx, cached);
569 }
570 }
571 }
572 return ctx.GetUndefined();
573 }
574
recurse_children(const SCENE_NS::INode::Ptr startNode,BASE_NS::string_view path)575 SCENE_NS::INode::Ptr recurse_children(const SCENE_NS::INode::Ptr startNode, BASE_NS::string_view path)
576 {
577 SCENE_NS::INode::Ptr node = startNode;
578 while (node != nullptr) {
579 node->BuildChildren(SCENE_NS::INode::BuildBehavior::NODE_BUILD_ONLY_DIRECT_CHILDREN);
580 // see if
581 if (auto container = interface_cast<META_NS::IContainer>(node)) {
582 auto pos = path.find('/', 0);
583 BASE_NS::string_view step = path.substr(0, pos);
584 node = interface_pointer_cast<SCENE_NS::INode>(container->FindByName(step));
585 if (node) {
586 if (pos == BASE_NS::string_view::npos) {
587 return node;
588 }
589 path = path.substr(pos + 1);
590 }
591 }
592 }
593 return {};
594 }
GetNodeByPath(NapiApi::FunctionContext<BASE_NS::string> & ctx)595 napi_value NodeImpl::GetNodeByPath(NapiApi::FunctionContext<BASE_NS::string>& ctx)
596 {
597 BASE_NS::string path = ctx.Arg<0>();
598 auto meta = GetThisNativeObject(ctx);
599 if (!meta) {
600 return ctx.GetNull();
601 }
602
603 META_NS::IObject::Ptr child;
604 if (auto node = interface_pointer_cast<SCENE_NS::INode>(meta)) {
605 ExecSyncTask([node, &child, path]() {
606 // make sure the child objects exist
607 child = interface_pointer_cast<META_NS::IObject>(recurse_children(node, path));
608 return META_NS::IAny::Ptr {};
609 });
610 }
611
612 if (!child) {
613 // no such child.
614 return ctx.GetNull();
615 }
616
617 if (auto cached = FetchJsObj(child)) {
618 // always return the same js object.
619 return cached;
620 }
621
622 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
623 CORE_LOG_F("INVALID SCENE!");
624 }
625
626 // create new js object for the native node.
627 NapiApi::Object argJS(ctx);
628 napi_value args[] = { scene_.GetValue(), argJS };
629
630 return CreateFromNativeInstance(ctx, child, false /*these are owned by the scene*/, BASE_NS::countof(args), args);
631 }
632