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 }