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