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