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