• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/interface/intf_scene.h>
24 #include <scene/ext/component_util.h>
25 #include <scene/interface/component_util.h>
26 #include <scene/interface/intf_layer.h>
27 #include <scene/interface/intf_light.h>
28 
29 #include <3d/ecs/components/layer_component.h>
30 
31 #include "BaseObjectJS.h"
32 #include "JsObjectCache.h"
33 
RegisterEnums(NapiApi::Object exports)34 void NodeImpl::RegisterEnums(NapiApi::Object exports)
35 {
36     napi_value v;
37     NapiApi::Object NodeType(exports.GetEnv());
38 #define DECL_ENUM(enu, x)                                            \
39     {                                                                \
40         napi_create_uint32(enu.GetEnv(), NodeImpl::NodeType::x, &v); \
41         enu.Set(#x, v);                                              \
42     }
43     DECL_ENUM(NodeType, NODE);
44     DECL_ENUM(NodeType, GEOMETRY);
45     DECL_ENUM(NodeType, CAMERA);
46     DECL_ENUM(NodeType, LIGHT);
47     DECL_ENUM(NodeType, TEXT);
48 #undef DECL_ENUM
49     exports.Set("NodeType", NodeType);
50 }
NodeImpl(NodeType type)51 NodeImpl::NodeImpl(NodeType type) : SceneResourceImpl(SceneResourceImpl::NODE), type_(type)
52 {
53     LOG_V("NodeImpl ++");
54 }
~NodeImpl()55 NodeImpl::~NodeImpl()
56 {
57     LOG_V("NodeImpl --");
58     posProxy_.reset();
59     sclProxy_.reset();
60     rotProxy_.reset();
61 }
GetInstanceImpl(uint32_t id)62 void* NodeImpl::GetInstanceImpl(uint32_t id)
63 {
64     if (id == NodeImpl::ID)
65         return this;
66     return SceneResourceImpl::GetInstanceImpl(id);
67 }
GetPropertyDescs(BASE_NS::vector<napi_property_descriptor> & props)68 void NodeImpl::GetPropertyDescs(BASE_NS::vector<napi_property_descriptor>& props)
69 {
70     using namespace NapiApi;
71     // META_NS::IContainer;
72 
73     SceneResourceImpl::GetPropertyDescs(props);
74 
75     // override "name" from sceneresource
76     for (auto it = props.begin(); it != props.end(); it++) {
77         if (BASE_NS::string_view("name").compare(it->utf8name) == 0) {
78             props.erase(it);
79             break;
80         }
81     }
82     props.push_back(
83         TROGetSetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetNodeName, &NodeImpl::SetNodeName>("name"));
84 
85     // node
86 
87     props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetPath>("path"));
88     props.push_back(TROGetSetProperty<Object, NodeImpl, &NodeImpl::GetPosition, &NodeImpl::SetPosition>("position"));
89     props.push_back(TROGetSetProperty<Object, NodeImpl, &NodeImpl::GetRotation, &NodeImpl::SetRotation>("rotation"));
90     props.push_back(TROGetSetProperty<Object, NodeImpl, &NodeImpl::GetScale, &NodeImpl::SetScale>("scale"));
91     props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetParent>("parent"));
92     props.push_back(TROGetSetProperty<bool, NodeImpl, &NodeImpl::GetVisible, &NodeImpl::SetVisible>("visible"));
93     props.push_back(TROGetSetProperty<bool, NodeImpl, &NodeImpl::GetChildContainer, &NodeImpl::SetVisible>("children"));
94     props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetNodeType>("nodeType"));
95     props.push_back(TROGetProperty<BASE_NS::string, NodeImpl, &NodeImpl::GetLayerMask>("layerMask"));
96 
97     // node methods
98     props.push_back(MakeTROMethod<FunctionContext<>, NodeImpl, &NodeImpl::Dispose>("destroy"));
99     props.push_back(
100         MakeTROMethod<FunctionContext<BASE_NS::string>, NodeImpl, &NodeImpl::GetNodeByPath>("getNodeByPath"));
101     props.push_back(
102         MakeTROMethod<FunctionContext<BASE_NS::string>, NodeImpl, &NodeImpl::GetComponent>("getComponent"));
103 
104     // layermask methods.
105     props.push_back(MakeTROMethod<FunctionContext<uint32_t>, NodeImpl, &NodeImpl::GetLayerMaskEnabled>("getEnabled"));
106     props.push_back(
107         MakeTROMethod<FunctionContext<uint32_t, bool>, NodeImpl, &NodeImpl::SetLayerMaskEnabled>("setEnabled"));
108 
109     // container
110     props.push_back(MakeTROMethod<FunctionContext<Object>, NodeImpl, &NodeImpl::AppendChild>("append"));
111     props.push_back(
112         MakeTROMethod<FunctionContext<Object, Object>, NodeImpl, &NodeImpl::InsertChildAfter>("insertAfter"));
113     props.push_back(MakeTROMethod<FunctionContext<Object>, NodeImpl, &NodeImpl::RemoveChild>("remove"));
114     props.push_back(MakeTROMethod<FunctionContext<uint32_t>, NodeImpl, &NodeImpl::GetChild>("get"));
115     props.push_back(MakeTROMethod<FunctionContext<>, NodeImpl, &NodeImpl::ClearChildren>("clear"));
116     props.push_back(MakeTROMethod<FunctionContext<>, NodeImpl, &NodeImpl::GetCount>("count"));
117 }
Dispose(NapiApi::FunctionContext<> & ctx)118 napi_value NodeImpl::Dispose(NapiApi::FunctionContext<>& ctx)
119 {
120     // Dispose of the native object. (makes the js object invalid)
121     posProxy_.reset();
122     sclProxy_.reset();
123     rotProxy_.reset();
124     SceneResourceImpl::Dispose(ctx);
125     scene_.Reset();
126     return ctx.GetUndefined();
127 }
GetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t> & ctx)128 napi_value NodeImpl::GetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t>& ctx)
129 {
130     if (!validateSceneRef()) {
131         return ctx.GetUndefined();
132     }
133     uint32_t bit = ctx.Arg<0>();
134     bool enabled = true;
135     if (auto node = ctx.This().GetNative<SCENE_NS::INode>()) {
136         uint64_t mask = 1ull << bit;
137         if (auto comp = SCENE_NS::GetComponent<SCENE_NS::ILayer>(node)) {
138             enabled = comp->LayerMask()->GetValue() & mask;
139         }
140     }
141     return ctx.GetBoolean(enabled);
142 }
SetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t,bool> & ctx)143 napi_value NodeImpl::SetLayerMaskEnabled(NapiApi::FunctionContext<uint32_t, bool>& ctx)
144 {
145     if (!validateSceneRef()) {
146         return ctx.GetUndefined();
147     }
148     uint32_t bit = ctx.Arg<0>();
149     bool enabled = ctx.Arg<1>();
150     if (auto node = ctx.This().GetNative<SCENE_NS::INode>()) {
151         if (!SCENE_NS::GetComponent<SCENE_NS::ILayer>(node)) {
152             SCENE_NS::AddComponent<CORE3D_NS::ILayerComponentManager>(node);
153         }
154         if (auto layer = SCENE_NS::GetComponent<SCENE_NS::ILayer>(node)) {
155             uint64_t mask = 1ull << bit;
156             if (enabled) {
157                 layer->LayerMask()->SetValue(layer->LayerMask()->GetValue() | mask);
158             } else {
159                 layer->LayerMask()->SetValue(layer->LayerMask()->GetValue() & ~mask);
160             }
161         }
162     }
163     return ctx.GetUndefined();
164 }
165 
GetNodeType(NapiApi::FunctionContext<> & ctx)166 napi_value NodeImpl::GetNodeType(NapiApi::FunctionContext<>& ctx)
167 {
168     if (!validateSceneRef()) {
169         return ctx.GetUndefined();
170     }
171     uint32_t type = -1; // return -1 if the object does not exist anymore
172     if (ctx.This().GetNative<SCENE_NS::INode>()) {
173         type = type_;
174     }
175     return ctx.GetNumber(type);
176 }
177 
GetLayerMask(NapiApi::FunctionContext<> & ctx)178 napi_value NodeImpl::GetLayerMask(NapiApi::FunctionContext<>& ctx)
179 {
180     if (!validateSceneRef()) {
181         return ctx.GetUndefined();
182     }
183 
184     if (const auto nodeJs = ctx.This(); nodeJs.GetNative<SCENE_NS::INode>()) {
185         return nodeJs.ToNapiValue();
186     }
187     return ctx.GetUndefined();
188 }
189 
GetNodeName(NapiApi::FunctionContext<> & ctx)190 napi_value NodeImpl::GetNodeName(NapiApi::FunctionContext<>& ctx)
191 {
192     if (!validateSceneRef()) {
193         return ctx.GetUndefined();
194     }
195 
196     BASE_NS::string name;
197     auto native = ctx.This().GetNative();
198     auto node = interface_pointer_cast<SCENE_NS::INode>(native);
199     if (!node) {
200         LOG_E("NodeImpl::GetNodeName NodeImpl not a node!");
201         return ctx.GetUndefined();
202     }
203     name = native->GetName();
204 
205     // LEGACY COMPATIBILITY start
206     auto parent = node->GetParent().GetResult();
207     if (!parent) {
208         //parentless node, "root"
209         if (name.empty()) {
210             // no name, and no parent.
211             // so return the legacy default name
212             name = "rootNode_";
213         }
214     }
215     // LEGACY COMPATIBILITY end
216 
217     return ctx.GetString(name);
218 }
SetNodeName(NapiApi::FunctionContext<BASE_NS::string> & ctx)219 void NodeImpl::SetNodeName(NapiApi::FunctionContext<BASE_NS::string>& ctx)
220 {
221     if (!validateSceneRef()) {
222         return;
223     }
224     BASE_NS::string name = ctx.Arg<0>();
225     if (auto node = ctx.This().GetNative<META_NS::INamed>()) {
226         node->Name()->SetValue(name);
227     } else {
228         LOG_W("renaming resource not implemented, trying to name (%d) to '%s'", type_, name.c_str());
229     }
230 }
231 
GetPath(NapiApi::FunctionContext<> & ctx)232 napi_value NodeImpl::GetPath(NapiApi::FunctionContext<>& ctx)
233 {
234     if (!validateSceneRef()) {
235         return ctx.GetUndefined();
236     }
237 
238     auto node = ctx.This().GetNative<SCENE_NS::INode>();
239     if (!node) {
240         // node does not exist anymore?
241         return ctx.GetUndefined();
242     }
243 
244     auto parent = node->GetParent().GetResult();
245     if (!parent) {
246         // parentless nodes are "root", and root nodes path is ofcourse empty.
247         return ctx.GetString("");
248     }
249     BASE_NS::string path = node->GetPath().GetResult();
250 
251     // LEGACY COMPATIBILITY start
252     // get root from scene..
253     NapiApi::Object obj = scene_.GetObject();
254     NapiApi::Object root = obj.Get<NapiApi::Object>("root");
255     // get the name of the root..
256     BASE_NS::string rootName = root.Get<BASE_NS::string>("name");
257 
258     // remove node name from path.
259     path = path.substr(0, path.find_last_of('/') + 1);
260     // make sure root node name is there.. (hack)
261 
262     // see if root name is in the path.
263     auto pos = path.find(rootName);
264     if (pos == BASE_NS::string::npos) {
265         // rootname missing from path.
266         path.insert(1, rootName.c_str());
267     }
268     // LEGACY COMPATIBILITY end
269     return ctx.GetString(path);
270 }
271 
GetVisible(NapiApi::FunctionContext<> & ctx)272 napi_value NodeImpl::GetVisible(NapiApi::FunctionContext<>& ctx)
273 {
274     if (!validateSceneRef()) {
275         return ctx.GetUndefined();
276     }
277 
278     auto node = ctx.This().GetNative<SCENE_NS::INode>();
279     bool visible = false;
280     if (node) {
281         visible = node->Enabled()->GetValue();
282     }
283     return ctx.GetBoolean(visible);
284 }
SetVisible(NapiApi::FunctionContext<bool> & ctx)285 void NodeImpl::SetVisible(NapiApi::FunctionContext<bool>& ctx)
286 {
287     if (!validateSceneRef()) {
288         return;
289     }
290 
291     auto node = ctx.This().GetNative<SCENE_NS::INode>();
292     if (node) {
293         bool visible = ctx.Arg<0>();
294         node->Enabled()->SetValue(visible);
295     }
296 }
297 
GetPosition(NapiApi::FunctionContext<> & ctx)298 napi_value NodeImpl::GetPosition(NapiApi::FunctionContext<>& ctx)
299 {
300     if (!validateSceneRef()) {
301         return ctx.GetUndefined();
302     }
303 
304     auto node = ctx.This().GetNative<SCENE_NS::INode>();
305     if (!node) {
306         return ctx.GetUndefined();
307     }
308     if (posProxy_ == nullptr) {
309         posProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx.Env(), node->Position());
310     }
311     return posProxy_->Value();
312 }
313 
SetPosition(NapiApi::FunctionContext<NapiApi::Object> & ctx)314 void NodeImpl::SetPosition(NapiApi::FunctionContext<NapiApi::Object>& ctx)
315 {
316     if (!validateSceneRef()) {
317         return;
318     }
319 
320     auto node = ctx.This().GetNative<SCENE_NS::INode>();
321     if (!node) {
322         return;
323     }
324     NapiApi::Object obj = ctx.Arg<0>();
325     if (posProxy_ == nullptr) {
326         posProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx.Env(), node->Position());
327     }
328     posProxy_->SetValue(obj);
329 }
330 
GetScale(NapiApi::FunctionContext<> & ctx)331 napi_value NodeImpl::GetScale(NapiApi::FunctionContext<>& ctx)
332 {
333     if (!validateSceneRef()) {
334         return ctx.GetUndefined();
335     }
336 
337     auto node = ctx.This().GetNative<SCENE_NS::INode>();
338     if (!node) {
339         return ctx.GetUndefined();
340     }
341     if (sclProxy_ == nullptr) {
342         sclProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx.Env(), node->Scale());
343     }
344     return sclProxy_->Value();
345 }
346 
SetScale(NapiApi::FunctionContext<NapiApi::Object> & ctx)347 void NodeImpl::SetScale(NapiApi::FunctionContext<NapiApi::Object>& ctx)
348 {
349     if (!validateSceneRef()) {
350         return;
351     }
352 
353     auto node = ctx.This().GetNative<SCENE_NS::INode>();
354     if (!node) {
355         return;
356     }
357     NapiApi::Object obj = ctx.Arg<0>();
358     if (sclProxy_ == nullptr) {
359         sclProxy_ = BASE_NS::make_unique<Vec3Proxy>(ctx.Env(), node->Scale());
360     }
361     sclProxy_->SetValue(obj);
362 }
363 
GetRotation(NapiApi::FunctionContext<> & ctx)364 napi_value NodeImpl::GetRotation(NapiApi::FunctionContext<>& ctx)
365 {
366     if (!validateSceneRef()) {
367         return ctx.GetUndefined();
368     }
369 
370     auto node = ctx.This().GetNative<SCENE_NS::INode>();
371     if (!node) {
372         return ctx.GetUndefined();
373     }
374     if (rotProxy_ == nullptr) {
375         rotProxy_ = BASE_NS::make_unique<QuatProxy>(ctx.Env(), node->Rotation());
376     }
377     return rotProxy_->Value();
378 }
379 
SetRotation(NapiApi::FunctionContext<NapiApi::Object> & ctx)380 void NodeImpl::SetRotation(NapiApi::FunctionContext<NapiApi::Object>& ctx)
381 {
382     if (!validateSceneRef()) {
383         return;
384     }
385 
386     auto node = ctx.This().GetNative<SCENE_NS::INode>();
387     if (!node) {
388         return;
389     }
390     NapiApi::Object obj = ctx.Arg<0>();
391     if (rotProxy_ == nullptr) {
392         rotProxy_ = BASE_NS::make_unique<QuatProxy>(ctx.Env(), node->Rotation());
393     }
394     rotProxy_->SetValue(obj);
395 }
396 
GetParent(NapiApi::FunctionContext<> & ctx)397 napi_value NodeImpl::GetParent(NapiApi::FunctionContext<>& ctx)
398 {
399     if (!validateSceneRef()) {
400         return ctx.GetUndefined();
401     }
402 
403     auto node = ctx.This().GetNative<SCENE_NS::INode>();
404     if (!node) {
405         return ctx.GetNull();
406     }
407 
408     auto parent = node->GetParent().GetResult();
409     if (!parent) {
410         // no parent. (root node)
411         return ctx.GetNull();
412     }
413 
414     if (!scene_.GetObject().GetNative()) {
415         LOG_F("INVALID SCENE!");
416     }
417 
418     NapiApi::Env env(ctx.GetEnv());
419     NapiApi::Object argJS(env);
420     napi_value args[] = { scene_.GetValue(), argJS.ToNapiValue() };
421 
422     // These are owned by the scene, so store only a weak reference.
423     auto js = CreateFromNativeInstance(env, parent, PtrType::WEAK, args);
424     if (auto wrapper = js.GetJsWrapper<NodeImpl>()) {
425         wrapper->Attached(true);
426     }
427     return js.ToNapiValue();
428 }
429 
GetChildContainer(NapiApi::FunctionContext<> & ctx)430 napi_value NodeImpl::GetChildContainer(NapiApi::FunctionContext<>& ctx)
431 {
432     if (!validateSceneRef()) {
433         return ctx.GetUndefined();
434     }
435 
436     // Node implements Container<Node>
437     return ctx.This().ToNapiValue();
438 }
439 
GetChild(NapiApi::FunctionContext<uint32_t> & ctx)440 napi_value NodeImpl::GetChild(NapiApi::FunctionContext<uint32_t>& ctx)
441 {
442     if (!validateSceneRef()) {
443         LOG_F("INVALID SCENE!");
444         return ctx.GetUndefined();
445     }
446 
447     uint32_t index = ctx.Arg<0>();
448     if (auto node = ctx.This().GetNative<SCENE_NS::INode>()) {
449         auto children = node->GetChildren().GetResult();
450         if (index < children.size()) {
451             const auto env = ctx.GetEnv();
452             auto argJS = NapiApi::Object { env };
453             napi_value args[] = { scene_.GetValue(), argJS.ToNapiValue() };
454             // These are owned by the scene, so store only weak reference.
455             auto js = CreateFromNativeInstance(env, children[index], PtrType::WEAK, args);
456             if (auto wrapper = js.GetJsWrapper<NodeImpl>()) {
457                 wrapper->Attached(true);
458             }
459             return js.ToNapiValue();
460         }
461     }
462     return ctx.GetNull();
463 }
464 
GetCount(NapiApi::FunctionContext<> & ctx)465 napi_value NodeImpl::GetCount(NapiApi::FunctionContext<>& ctx)
466 {
467     if (!validateSceneRef()) {
468         return ctx.GetUndefined();
469     }
470 
471     uint32_t count = 0;
472     if (auto node = ctx.This().GetNative<SCENE_NS::INode>()) {
473         count = node->GetChildren().GetResult().size();
474     }
475     return ctx.GetNumber(count);
476 }
AppendChild(NapiApi::FunctionContext<NapiApi::Object> & ctx)477 napi_value NodeImpl::AppendChild(NapiApi::FunctionContext<NapiApi::Object>& ctx)
478 {
479     if (!validateSceneRef()) {
480         return ctx.GetUndefined();
481     }
482     auto arg0 = ctx.Arg<0>();
483     if (arg0.IsUndefinedOrNull()) {
484         // okay. Invalid arg error?
485         return ctx.GetUndefined();
486     }
487 
488     NapiApi::Object childJS = arg0;
489 
490     auto childNode = childJS.GetNative<SCENE_NS::INode>();
491     if (!childNode) {
492         return ctx.GetUndefined();
493     }
494 
495     if (auto parent = ctx.This().GetNative<SCENE_NS::INode>()) {
496         if (auto wrapper = childJS.GetJsWrapper<NodeImpl>()) {
497             wrapper->Attached(true);
498         }
499         parent->AddChild(childNode);
500         childNode->Enabled()->SetValue(true);
501     }
502 
503     // make the js object keep a weak ref again (scene keeps the native object alive)
504     // (or move ownership back from SceneJS? and remove dispose hook?)
505     if (auto tro = childJS.GetRoot()) {
506         if (auto native = tro->GetNativeObject()) {
507             tro->SetNativeObject(native, PtrType::WEAK);
508         }
509     }
510 
511     return ctx.GetUndefined();
512 }
InsertChildAfter(NapiApi::FunctionContext<NapiApi::Object,NapiApi::Object> & ctx)513 napi_value NodeImpl::InsertChildAfter(NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object>& ctx)
514 {
515     if (!validateSceneRef()) {
516         return ctx.GetUndefined();
517     }
518     auto arg0 = ctx.Arg<0>();
519     if (arg0.IsUndefinedOrNull()) {
520         // okay. Invalid arg error?
521         return ctx.GetUndefined();
522     }
523 
524     NapiApi::Object childJS = arg0;
525     auto childNode = childJS.GetNative<SCENE_NS::INode>();
526     if (!childNode) {
527         return ctx.GetUndefined();
528     }
529 
530     auto arg1 = ctx.Arg<1>();
531     SCENE_NS::INode::Ptr siblingNode;
532     if (arg1.IsDefinedAndNotNull()) {
533         NapiApi::Object siblingJS = arg1;
534         siblingNode = siblingJS.GetNative<SCENE_NS::INode>();
535     }
536 
537     if (auto parent = ctx.This().GetNative<SCENE_NS::INode>()) {
538         size_t index = 0;
539         if (siblingNode) {
540             auto data = parent->GetChildren().GetResult();
541             for (auto d : data) {
542                 index++;
543                 if (d == siblingNode) {
544                     break;
545                 }
546             }
547         }
548         if (auto wrapper = childJS.GetJsWrapper<NodeImpl>()) {
549             wrapper->Attached(true);
550         }
551         parent->AddChild(childNode, index).GetResult();
552         childNode->Enabled()->SetValue(true);
553     }
554 
555     // make the js object keep a weak ref again (scene keeps the native object alive)
556     // (or move ownership back from SceneJS? and remove dispose hook?)
557     if (auto tro = childJS.GetRoot()) {
558         if (auto native = tro->GetNativeObject()) {
559             tro->SetNativeObject(native, PtrType::WEAK);
560         }
561     }
562     return ctx.GetUndefined();
563 }
564 
RemoveChild(NapiApi::FunctionContext<NapiApi::Object> & ctx)565 napi_value NodeImpl::RemoveChild(NapiApi::FunctionContext<NapiApi::Object>& ctx)
566 {
567     if (!validateSceneRef()) {
568         return ctx.GetUndefined();
569     }
570 
571     // okay. just detach from parent. (make parent invalid)
572     // and disable it.
573     auto arg0 = ctx.Arg<0>();
574     if (arg0.IsUndefinedOrNull()) {
575         // okay. Invalid arg error?
576         return ctx.GetUndefined();
577     }
578     NapiApi::Object childJS = arg0;
579 
580     auto childNode = childJS.GetNative<SCENE_NS::INode>();
581     if (!childNode) {
582         return ctx.GetUndefined();
583     }
584 
585     // make the js object keep a strong ref.
586     // (or give SceneJS ownership? and add dispose hook?)
587     if (auto tro = childJS.GetRoot()) {
588         if (auto native = tro->GetNativeObject()) {
589             tro->SetNativeObject(native, PtrType::STRONG);
590         }
591     }
592 
593     if (auto parent = ctx.This().GetNative<SCENE_NS::INode>()) {
594         if (auto wrapper = childJS.GetJsWrapper<NodeImpl>()) {
595             wrapper->Attached(false);
596         }
597         parent->RemoveChild(childNode).GetResult();
598         childNode->Enabled()->SetValue(false);
599     }
600     return ctx.GetUndefined();
601 }
602 
ClearChildren(NapiApi::FunctionContext<> & ctx)603 napi_value NodeImpl::ClearChildren(NapiApi::FunctionContext<>& ctx)
604 {
605     if (!validateSceneRef()) {
606         return ctx.GetUndefined();
607     }
608 
609     BASE_NS::vector<SCENE_NS::INode::Ptr> removedNodes;
610     if (auto parent = ctx.This().GetNative<SCENE_NS::INode>()) {
611         for (auto node : parent->GetChildren().GetResult()) {
612             if (auto childJS = FetchJsObj(node)) {
613                 if (auto wrapper = childJS.GetJsWrapper<NodeImpl>()) {
614                     wrapper->Attached(false);
615                 }
616             }
617             parent->RemoveChild(node).GetResult();
618             node->Enabled()->SetValue(false);
619             removedNodes.emplace_back(BASE_NS::move(node));
620         }
621 
622         for (auto node : removedNodes) {
623             if (auto cached = FetchJsObj(node)) {
624                 if (auto tro = cached.GetRoot()) {
625                     if (auto native = tro->GetNativeObject()) {
626                         tro->SetNativeObject(native, PtrType::STRONG);
627                     }
628                 }
629             }
630         }
631     }
632     return ctx.GetUndefined();
633 }
634 
GetNodeByPath(NapiApi::FunctionContext<BASE_NS::string> & ctx)635 napi_value NodeImpl::GetNodeByPath(NapiApi::FunctionContext<BASE_NS::string>& ctx)
636 {
637     if (!validateSceneRef()) {
638         LOG_F("INVALID SCENE!");
639         return ctx.GetUndefined();
640     }
641 
642     BASE_NS::string path = ctx.Arg<0>();
643     if (auto node = ctx.This().GetNative<SCENE_NS::INode>()) {
644         BASE_NS::string childPath = node->GetPath().GetResult() + "/" + path;
645         const auto child = node->GetScene()->FindNode(childPath).GetResult();
646         if (!child) {
647             return ctx.GetNull();
648         }
649         const auto env = ctx.GetEnv();
650         auto argJS = NapiApi::Object { env };
651         napi_value args[] = { scene_.GetValue(), argJS.ToNapiValue() };
652         // These are owned by the scene, so store only weak reference.
653         auto js = CreateFromNativeInstance(env, child, PtrType::WEAK, args);
654         if (js.GetJsWrapper<NodeImpl>()) {
655             js.GetJsWrapper<NodeImpl>()->Attached(true);
656             return js.ToNapiValue();
657         }
658     }
659     return ctx.GetNull();
660 }
661 
GetComponent(NapiApi::FunctionContext<BASE_NS::string> & ctx)662 napi_value NodeImpl::GetComponent(NapiApi::FunctionContext<BASE_NS::string>& ctx)
663 {
664     if (!validateSceneRef()) {
665         return ctx.GetUndefined();
666     }
667 
668     BASE_NS::string name = ctx.Arg<0>();
669     auto meta = ctx.This().GetNative();
670     if (!meta) {
671         return ctx.GetNull();
672     }
673 
674     if (auto att = interface_pointer_cast<META_NS::IAttach>(meta)) {
675         if (auto cont = att->GetAttachmentContainer(false)) {
676             if (auto comp = cont->FindByName<SCENE_NS::IComponent>(name)) {
677                 NapiApi::Env env(ctx.GetEnv());
678                 NapiApi::Object argJS(env);
679                 napi_value args[] = { scene_.GetValue(), argJS.ToNapiValue() };
680                 return CreateFromNativeInstance(env, comp, PtrType::WEAK, args).ToNapiValue();
681             }
682         }
683     }
684     return ctx.GetUndefined();
685 }
686 
IsAttached() const687 bool NodeImpl::IsAttached() const
688 {
689     return attached_;
690 }
Attached(bool attached)691 void NodeImpl::Attached(bool attached)
692 {
693     attached_ = attached;
694 }
695 
696