• 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 
16 #include "SceneJS.h"
17 
18 #include "LightJS.h"
19 #include "MaterialJS.h"
20 #include "MeshResourceJS.h"
21 #include "NodeJS.h"
22 #include "PromiseBase.h"
23 static constexpr BASE_NS::Uid IO_QUEUE { "be88e9a0-9cd8-45ab-be48-937953dc258f" };
24 
25 #include <meta/api/make_callback.h>
26 #include <meta/interface/animation/intf_animation.h>
27 #include <meta/interface/intf_object_context.h>
28 #include <meta/interface/intf_task_queue.h>
29 #include <meta/interface/intf_task_queue_registry.h>
30 #include <meta/interface/property/construct_property.h>
31 #include <meta/interface/property/property_events.h>
32 #include <scene/ext/intf_render_resource.h>
33 #include <scene/interface/intf_material.h>
34 #include <scene/interface/intf_mesh_resource.h>
35 #include <scene/interface/intf_node_import.h>
36 #include <scene/interface/intf_render_configuration.h>
37 #include <scene/interface/intf_scene.h>
38 #include <scene/interface/intf_scene_manager.h>
39 
40 #include <core/image/intf_image_loader_manager.h>
41 #include <core/intf_engine.h>
42 #include <render/device/intf_gpu_resource_manager.h>
43 #include <render/intf_render_context.h>
44 
45 #ifdef __SCENE_ADAPTER__
46 #include <parameters.h>
47 #include "3d_widget_adapter_log.h"
48 #include "scene_adapter/scene_adapter.h"
49 #endif
50 
51 // LEGACY COMPATIBILITY start
52 #include <geometry_definition/GeometryDefinition.h>
53 #include <scene/ext/intf_ecs_context.h>
54 #include <scene/ext/intf_ecs_object.h>
55 #include <scene/ext/intf_ecs_object_access.h>
56 
57 #include <3d/ecs/components/name_component.h>
58 #include <3d/ecs/components/node_component.h>
59 // fix names to match "ye olde" implementation
60 // the bug that unnamed nodes stops hierarchy creation also still exists, works around that issue too.
Fixnames(SCENE_NS::IScene::Ptr scene)61 void Fixnames(SCENE_NS::IScene::Ptr scene)
62 {
63     struct rr {
64         uint32_t id_ = 1;
65         // not actual tree, but map of entities, and their children.
66         BASE_NS::unordered_map<CORE_NS::Entity, BASE_NS::vector<CORE_NS::Entity>> tree;
67         BASE_NS::vector<CORE_NS::Entity> roots;
68         CORE3D_NS::INodeComponentManager* cm;
69         CORE3D_NS::INameComponentManager* nm;
70         explicit rr(SCENE_NS::IScene::Ptr scene)
71         {
72             CORE_NS::IEcs::Ptr ecs = scene->GetInternalScene()->GetEcsContext().GetNativeEcs();
73             cm = CORE_NS::GetManager<CORE3D_NS::INodeComponentManager>(*ecs);
74             nm = CORE_NS::GetManager<CORE3D_NS::INameComponentManager>(*ecs);
75             fix();
76         }
77         void scan()
78         {
79             const auto count = cm->GetComponentCount();
80             // collect nodes and their children.
81             tree.reserve(cm->GetComponentCount());
82             for (auto i = 0; i < count; i++) {
83                 auto enti = cm->GetEntity(i);
84                 // add node to our list. (if not yet added)
85                 tree.insert({ enti });
86                 auto parent = cm->Get(i).parent;
87                 if (CORE_NS::EntityUtil::IsValid(parent)) {
88                     tree[parent].push_back(enti);
89                 } else {
90                     // no parent, so it's a "root"
91                     roots.push_back(enti);
92                 }
93             }
94         }
95         void recurse(CORE_NS::Entity id)
96         {
97             CORE3D_NS::NameComponent c = nm->Get(id);
98             if (c.name.empty()) {
99                 // create a name for unnamed node.
100                 c.name = "Unnamed Node ";
101                 c.name += BASE_NS::to_string(id_++);
102                 nm->Set(id, c);
103             }
104             for (auto c : tree[id]) {
105                 recurse(c);
106             }
107         }
108         void fix()
109         {
110             scan();
111             for (auto i : roots) {
112                 id_ = 1;
113                 // force root node name to match legacy by default.
114                 for (auto c : tree[i]) {
115                     recurse(c);
116                 }
117             }
118         }
119     } r(scene);
120 }
121 // LEGACY COMPATIBILITY end
122 
123 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
124 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
125 
Init(napi_env env,napi_value exports)126 void SceneJS::Init(napi_env env, napi_value exports)
127 {
128     using namespace NapiApi;
129     // clang-format off
130     auto loadFun = [](napi_env e, napi_callback_info cb) -> napi_value {
131         FunctionContext<> fc(e, cb);
132         return SceneJS::Load(fc);
133     };
134 
135     napi_property_descriptor props[] = {
136         // static methods
137         napi_property_descriptor{ "load", nullptr, loadFun, nullptr, nullptr, nullptr,
138             (napi_property_attributes)(napi_static|napi_default_method)},
139         // properties
140         GetSetProperty<uint32_t, SceneJS, &SceneJS::GetRenderMode, &SceneJS::SetRenderMode>("renderMode"),
141         GetSetProperty<NapiApi::Object, SceneJS, &SceneJS::GetEnvironment, &SceneJS::SetEnvironment>("environment"),
142         GetProperty<NapiApi::Array, SceneJS, &SceneJS::GetAnimations>("animations"),
143         // animations
144         GetProperty<BASE_NS::string, SceneJS, &SceneJS::GetRoot>("root"),
145         // scene methods
146         Method<NapiApi::FunctionContext<BASE_NS::string>, SceneJS, &SceneJS::GetNode>("getNodeByPath"),
147         Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::GetResourceFactory>("getResourceFactory"),
148         Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::Dispose>("destroy"),
149 
150         // SceneResourceFactory methods
151         Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateCamera>("createCamera"),
152         Method<NapiApi::FunctionContext<NapiApi::Object, uint32_t>, SceneJS, &SceneJS::CreateLight>("createLight"),
153         Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateNode>("createNode"),
154         Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateTextNode>("createTextNode"),
155         Method<NapiApi::FunctionContext<NapiApi::Object, uint32_t>,
156             SceneJS, &SceneJS::CreateMaterial>("createMaterial"),
157         Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateShader>("createShader"),
158         Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateImage>("createImage"),
159         Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateEnvironment>("createEnvironment"),
160         Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::CreateScene>("createScene"),
161 
162         Method<NapiApi::FunctionContext<BASE_NS::string, NapiApi::Object, NapiApi::Object>, SceneJS,
163             &SceneJS::ImportNode>("importNode"),
164         Method<NapiApi::FunctionContext<BASE_NS::string, NapiApi::Object, NapiApi::Object>, SceneJS,
165             &SceneJS::ImportScene>("importScene"),
166         Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::RenderFrame>("renderFrame"),
167 
168         Method<FunctionContext<Object, Object>, SceneJS, &SceneJS::CreateMeshResource>("createMesh"),
169         Method<FunctionContext<Object, Object>, SceneJS, &SceneJS::CreateGeometry>("createGeometry"),
170     };
171     // clang-format on
172 
173     napi_value func;
174     auto status = napi_define_class(env, "Scene", NAPI_AUTO_LENGTH, BaseObject::ctor<SceneJS>(), nullptr,
175         sizeof(props) / sizeof(props[0]), props, &func);
176 
177     napi_set_named_property(env, exports, "Scene", func);
178 
179     NapiApi::MyInstanceState* mis;
180     GetInstanceData(env, reinterpret_cast<void**>(&mis));
181     mis->StoreCtor("Scene", func);
182 }
183 
RegisterEnums(NapiApi::Object exports)184 void SceneJS::RegisterEnums(NapiApi::Object exports)
185 {
186     napi_value v;
187     NapiApi::Object en(exports.GetEnv());
188 
189     napi_create_uint32(en.GetEnv(), static_cast<uint32_t>(SCENE_NS::RenderMode::IF_DIRTY), &v);
190     en.Set("RENDER_WHEN_DIRTY", v);
191     napi_create_uint32(en.GetEnv(), static_cast<uint32_t>(SCENE_NS::RenderMode::ALWAYS), &v);
192     en.Set("RENDER_CONTINUOUSLY", v);
193     napi_create_uint32(en.GetEnv(), static_cast<uint32_t>(SCENE_NS::RenderMode::MANUAL), &v);
194     en.Set("RENDER_MANUALLY", v);
195 
196     exports.Set("RenderMode", en);
197 }
198 
199 
FetchResourceOrUri(napi_env e,napi_value arg)200 BASE_NS::string FetchResourceOrUri(napi_env e, napi_value arg)
201 {
202     napi_valuetype type;
203     napi_typeof(e, arg, &type);
204     if (type == napi_string) {
205         BASE_NS::string uri = NapiApi::Value<BASE_NS::string>(e, arg);
206         // check if there is a protocol
207         auto t = uri.find("://");
208         if (t == BASE_NS::string::npos) {
209             // no proto . so use default
210             // set system file as default format
211             uri.insert(0, "file://");
212         }
213         return uri;
214     }
215     if (type == napi_object) {
216         NapiApi::Object resource(e, arg);
217         uint32_t id = resource.Get<uint32_t>("id");
218         uint32_t resourceType = resource.Get<uint32_t>("type");
219         NapiApi::Array parms = resource.Get<NapiApi::Array>("params");
220         BASE_NS::string uri;
221         if ((id == 0) && (resourceType == 30000)) { // 30000: param
222             // seems like a correct rawfile.
223             uri = parms.Get<BASE_NS::string>(0);
224         }
225         if (!uri.empty()) {
226             // add the schema then
227             uri.insert(0, "OhosRawFile://");
228         }
229         return uri;
230     }
231     return "";
232 }
FetchResourceOrUri(const NapiApi::Object obj)233 BASE_NS::string FetchResourceOrUri(const NapiApi::Object obj)
234 {
235     return FetchResourceOrUri(obj.GetEnv(), obj.ToNapiValue());
236 }
237 
FetchResourceOrUri(NapiApi::FunctionContext<> & ctx)238 BASE_NS::string FetchResourceOrUri(NapiApi::FunctionContext<>& ctx)
239 {
240     BASE_NS::string uri;
241     NapiApi::FunctionContext<NapiApi::Object> resourceContext(ctx);
242     NapiApi::FunctionContext<BASE_NS::string> uriContext(ctx);
243 
244     if (uriContext) {
245         // actually not supported anymore.
246         uri = uriContext.Arg<0>();
247         // check if there is a protocol
248         auto t = uri.find("://");
249         if (t == BASE_NS::string::npos) {
250             // no proto . so use default
251             uri.insert(0, "file://");
252         }
253     } else if (resourceContext) {
254         // get it from resource then
255         NapiApi::Object resource = resourceContext.Arg<0>();
256         uint32_t id = resource.Get<uint32_t>("id");
257         uint32_t type = resource.Get<uint32_t>("type");
258         NapiApi::Array parms = resource.Get<NapiApi::Array>("params");
259         // try to identify a $rawfile. (from resource object)
260         if ((id == 0) && (type == 30000)) { // 30000: param
261             // seems like a correct rawfile.
262             uri = parms.Get<BASE_NS::string>(0);
263         }
264         if (!uri.empty()) {
265             // add the schema then
266             uri.insert(0, "OhosRawFile://");
267         }
268     }
269     return uri;
270 }
271 
Load(NapiApi::FunctionContext<> & ctx)272 napi_value SceneJS::Load(NapiApi::FunctionContext<>& ctx)
273 {
274     BASE_NS::string uri = FetchResourceOrUri(ctx);
275     if (uri.empty()) {
276         uri = "scene://empty";
277     }
278     // make sure slashes are correct.. *eh*
279     for (;;) {
280         auto t = uri.find_first_of('\\');
281         if (t == BASE_NS::string::npos) {
282             break;
283         }
284         uri[t] = '/';
285     }
286 
287     struct Promise : public PromiseBase {
288         using PromiseBase::PromiseBase;
289         BASE_NS::string uri;
290         SCENE_NS::IScene::Ptr scene;
291         bool SetResult() override
292         {
293             if (scene) {
294                 auto obj = interface_pointer_cast<META_NS::IObject>(scene);
295                 auto jsscene = CreateJsObj(env_, "Scene", obj, true, 0, nullptr);
296                 // link the native object to js object.
297                 StoreJsObj(obj, jsscene);
298                 result_ = jsscene.ToNapiValue();
299 
300                 auto curenv = jsscene.Get<NapiApi::Object>("environment");
301                 if (curenv.IsUndefinedOrNull()) {
302                     // setup default env
303                     NapiApi::Object argsIn(env_);
304                     argsIn.Set("name", "DefaultEnv");
305 
306                     auto* tro = static_cast<SceneJS*>(jsscene.Native<TrueRootObject>());
307                     if (tro == nullptr) {
308                         LOG_E("tro is nullptr");
309                         return false;
310                     }
311                     auto res = tro->CreateEnvironment(jsscene, argsIn);
312                     res.Set("backgroundType", NapiApi::Value<uint32_t>(env_, 1)); // image.. but with null.
313                     jsscene.Set("environment", res);
314                 }
315                 for (auto&& c : scene->GetCameras().GetResult()) {
316                     c->RenderingPipeline()->SetValue(SCENE_NS::CameraPipeline::FORWARD);
317                     c->ColorTargetCustomization()->SetValue(
318                         { SCENE_NS::ColorFormat{BASE_NS::BASE_FORMAT_R16G16B16A16_SFLOAT}});
319                 }
320 
321 #ifdef __SCENE_ADAPTER__
322                 // set SceneAdapter
323                 auto oo = GetRootObject(NapiApi::Object(env_, result_));
324                 if (oo == nullptr) {
325                     CORE_LOG_E("GetRootObject return nullptr in SceneJS Load.");
326                     return false;
327                 }
328 
329                 auto sceneAdapter = std::make_shared<OHOS::Render3D::SceneAdapter>();
330                 sceneAdapter->SetSceneObj(oo->GetNativeObject());
331                 auto sceneJs = static_cast<SceneJS*>(oo);
332                 sceneJs->scene_ = sceneAdapter;
333 #endif
334                 return true;
335             }
336             return false;
337         };
338     };
339     Promise* promise = new Promise(ctx.Env());
340     promise->uri = uri;
341 
342     auto jsPromise = promise->ToNapiValue();
343 
344     auto params = interface_pointer_cast<META_NS::IMetadata>(META_NS::GetObjectRegistry().GetDefaultObjectContext());
345     auto m = META_NS::GetObjectRegistry().Create<SCENE_NS::ISceneManager>(SCENE_NS::ClassId::SceneManager, params);
346     if (m) {
347         m->CreateScene(uri).Then(
348             [promise](SCENE_NS::IScene::Ptr scene) {
349                 if (scene) {
350                     promise->scene = scene;
351                     auto& obr = META_NS::GetObjectRegistry();
352                     // make sure we have renderconfig
353                     auto rc = scene->RenderConfiguration()->GetValue();
354                     if (!rc) {
355                         LOG_F("No render config");
356                     }
357                     // Make sure there's a valid root node
358                     scene->GetInternalScene()->GetEcsContext().CreateUnnamedRootNode();
359                     // LEGACY COMPATIBILITY start
360                     Fixnames(scene);
361                     // LEGACY COMPATIBILITY end
362                     AddScene(&obr, scene);
363                 }
364                 promise->SettleLater();
365             },
366             nullptr);
367     } else {
368         promise->Reject();
369     }
370     return jsPromise;
371 }
372 
RenderFrame(NapiApi::FunctionContext<> & ctx)373 napi_value SceneJS::RenderFrame(NapiApi::FunctionContext<>& ctx)
374 {
375     if (ctx.ArgCount() > 1) {
376         CORE_LOG_E("render frame %d", __LINE__);
377         return ctx.GetUndefined();
378     }
379     bool res = true;
380 #ifdef __SCENE_ADAPTER__
381     auto sceneAdapter = std::static_pointer_cast<OHOS::Render3D::SceneAdapter>(scene_);
382     if (sceneAdapter) {
383         sceneAdapter->SetNeedsRepaint(false);
384         sceneAdapter->RenderFrame(false);
385     }
386 #endif
387     return ctx.GetBoolean(res);
388 }
389 
Dispose(NapiApi::FunctionContext<> & ctx)390 napi_value SceneJS::Dispose(NapiApi::FunctionContext<>& ctx)
391 {
392     DisposeNative(nullptr);
393 #ifdef __SCENE_ADAPTER__
394     if (scene_) {
395         scene_->Deinit();
396     }
397 #endif
398     return {};
399 }
DisposeNative(void *)400 void SceneJS::DisposeNative(void*)
401 {
402     if (!disposed_) {
403         disposed_ = true;
404         LOG_V("SCENE_JS::DisposeNative");
405 
406         NapiApi::Object scen(env_);
407         napi_value tmp;
408         napi_create_external(
409             env_, static_cast<void*>(this),
410             [](napi_env env, void* data, void* finalize_hint) {
411                 // do nothing.
412             },
413             nullptr, &tmp);
414         scen.Set("SceneJS", tmp);
415         napi_value scene = scen.ToNapiValue();
416 
417         // dispose active environment
418         if (auto env = environmentJS_.GetObject()) {
419             NapiApi::Function func = env.Get<NapiApi::Function>("destroy");
420             if (func) {
421                 func.Invoke(env, 1, &scene);
422             }
423         }
424         environmentJS_.Reset();
425 
426         // dispose all cameras/env/etcs.
427         while (!strongDisposables_.empty()) {
428             auto it = strongDisposables_.begin();
429             auto token = it->first;
430             auto env = it->second.GetObject();
431             if (env) {
432                 NapiApi::Function func = env.Get<NapiApi::Function>("destroy");
433                 if (func) {
434                     func.Invoke(env, 1, &scene);
435                 }
436             } else {
437                 strongDisposables_.erase(strongDisposables_.begin());
438             }
439         }
440 
441         // dispose
442         while (!disposables_.empty()) {
443             auto env = disposables_.begin()->second.GetObject();
444             if (env) {
445                 NapiApi::Function func = env.Get<NapiApi::Function>("destroy");
446                 if (func) {
447                     func.Invoke(env, 1, &scene);
448                 }
449             } else {
450                 disposables_.erase(disposables_.begin());
451             }
452         }
453 
454         for (auto b : bitmaps_) {
455             b.second.reset();
456         }
457         if (auto nativeScene = interface_pointer_cast<SCENE_NS::IScene>(GetNativeObject())) {
458             // reset the native object refs
459             SetNativeObject(nullptr, false);
460             SetNativeObject(nullptr, true);
461 
462             auto r = nativeScene->RenderConfiguration()->GetValue();
463             if (r) {
464                 r->Environment()->SetValue(nullptr);
465                 r.reset();
466             }
467             FlushScenes();
468         }
469     }
470 }
GetInstanceImpl(uint32_t id)471 void* SceneJS::GetInstanceImpl(uint32_t id)
472 {
473     if (id == SceneJS::ID)
474         return this;
475     return nullptr;
476 }
Finalize(napi_env env)477 void SceneJS::Finalize(napi_env env)
478 {
479     DisposeNative(nullptr);
480     BaseObject<SceneJS>::Finalize(env);
481 }
482 
AddScene(META_NS::IObjectRegistry * obr,SCENE_NS::IScene::Ptr scene)483 void SceneJS::AddScene(META_NS::IObjectRegistry* obr, SCENE_NS::IScene::Ptr scene)
484 {
485     if (!obr) {
486         return;
487     }
488     auto params = interface_pointer_cast<META_NS::IMetadata>(obr->GetDefaultObjectContext());
489     if (!params) {
490         return;
491     }
492     auto duh = params->GetArrayProperty<IntfWeakPtr>("Scenes");
493     if (!duh) {
494         return;
495     }
496     duh->AddValue(interface_pointer_cast<CORE_NS::IInterface>(scene));
497 }
498 
SceneJS(napi_env e,napi_callback_info i)499 SceneJS::SceneJS(napi_env e, napi_callback_info i) : BaseObject<SceneJS>(e, i)
500 {
501     LOG_V("SceneJS ++");
502     env_ = e; // store..
503     NapiApi::FunctionContext<NapiApi::Object> fromJs(e, i);
504     if (!fromJs) {
505         // okay internal create. we will receive the object after.
506         return;
507     }
508     // SceneJS should be created by "Scene.Load" only.
509     // so this never happens.
510 }
511 
FlushScenes()512 void SceneJS::FlushScenes()
513 {
514     ExecSyncTask([]() {
515         auto& obr = META_NS::GetObjectRegistry();
516         if (auto params = interface_pointer_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext())) {
517             if (auto duh = params->GetArrayProperty<IntfWeakPtr>("Scenes")) {
518                 for (auto i = 0; i < duh->GetSize();) {
519                     auto w = duh->GetValueAt(i);
520                     if (w.lock() == nullptr) {
521                         duh->RemoveAt(i);
522                     } else {
523                         i++;
524                     }
525                 }
526             }
527         }
528         return META_NS::IAny::Ptr {};
529     });
530 }
~SceneJS()531 SceneJS::~SceneJS()
532 {
533     LOG_V("SceneJS --");
534     DisposeNative(nullptr);
535     // flush all null scene objects here too.
536     FlushScenes();
537     if (!GetNativeObject()) {
538         return;
539     }
540 }
541 
GetNode(NapiApi::FunctionContext<BASE_NS::string> & ctx)542 napi_value SceneJS::GetNode(NapiApi::FunctionContext<BASE_NS::string>& ctx)
543 {
544     // verify that path starts from "correct root" and then let the root node handle the rest.
545     NapiApi::Object meJs(ctx.This());
546     NapiApi::Object root = meJs.Get<NapiApi::Object>("root");
547     BASE_NS::string rootName = root.Get<BASE_NS::string>("name");
548     NapiApi::Function func = root.Get<NapiApi::Function>("getNodeByPath");
549     BASE_NS::string path = ctx.Arg<0>();
550     if (path.empty() || (path == BASE_NS::string_view("/")) || (path==rootName)) {
551         // empty or '/' or "exact rootnodename". so return root
552         return root.ToNapiValue();
553     }
554 
555     // remove the "root nodes name", if given (make sure it also matches though..)
556     auto pos = 0;
557     if (path[0] != '/') {
558         pos = path.find('/', 0);
559         BASE_NS::string_view step = path.substr(0, pos);
560         if (!step.empty() && (step != rootName)) {
561             // root not matching
562             return ctx.GetNull();
563         }
564     }
565     if (pos != BASE_NS::string_view::npos) {
566         path = path.substr(pos + 1);
567     }
568 
569     if (path.empty()) {
570         // after removing the root node name
571         // nothing left in path, so return root.
572         return root.ToNapiValue();
573     }
574 
575     napi_value newpath = ctx.GetString(path);
576     if (newpath) {
577         return func.Invoke(root, 1, &newpath);
578     }
579     return ctx.GetNull();
580 }
GetRoot(NapiApi::FunctionContext<> & ctx)581 napi_value SceneJS::GetRoot(NapiApi::FunctionContext<>& ctx)
582 {
583     if (auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject())) {
584         SCENE_NS::INode::Ptr root = scene->GetRootNode().GetResult();
585         auto obj = interface_pointer_cast<META_NS::IObject>(root);
586 
587         if (auto cached = FetchJsObj(obj)) {
588             // always return the same js object.
589             return cached.ToNapiValue();
590         }
591 
592         NapiApi::StrongRef sceneRef { ctx.This() };
593         if (!GetNativeMeta<SCENE_NS::IScene>(sceneRef.GetObject())) {
594             LOG_F("INVALID SCENE!");
595         }
596 
597         NapiApi::Object argJS(ctx.GetEnv());
598         napi_value args[] = { sceneRef.GetObject().ToNapiValue(), argJS.ToNapiValue() };
599 
600         auto js = CreateFromNativeInstance(
601             ctx.GetEnv(), obj, false /*these are owned by the scene*/, BASE_NS::countof(args), args);
602         if (auto nm = GetJsWrapper<NodeImpl>(js)) {
603             nm->Attached(true);
604         }
605         return js.ToNapiValue();
606     }
607     return ctx.GetUndefined();
608 }
609 
GetEnvironment(NapiApi::FunctionContext<> & ctx)610 napi_value SceneJS::GetEnvironment(NapiApi::FunctionContext<>& ctx)
611 {
612     if (auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject())) {
613         SCENE_NS::IEnvironment::Ptr environment;
614         auto rc = scene->RenderConfiguration()->GetValue();
615         if (rc) {
616             environment = rc->Environment()->GetValue();
617         }
618         if (environment) {
619             auto obj = interface_pointer_cast<META_NS::IObject>(environment);
620             if (auto cached = FetchJsObj(obj)) {
621                 // always return the same js object.
622                 return cached.ToNapiValue();
623             }
624 
625             NapiApi::StrongRef sceneRef { ctx.This() };
626             if (!GetNativeMeta<SCENE_NS::IScene>(sceneRef.GetObject())) {
627                 LOG_F("INVALID SCENE!");
628             }
629 
630             NapiApi::Env env(ctx.Env());
631             NapiApi::Object argJS(env);
632             napi_value args[] = { sceneRef.GetObject().ToNapiValue(), argJS.ToNapiValue() };
633 
634             environmentJS_ =
635                 NapiApi::StrongRef(CreateFromNativeInstance(env, obj, false, BASE_NS::countof(args), args));
636             return environmentJS_.GetValue();
637         }
638     }
639     return ctx.GetNull();
640 }
641 
SetEnvironment(NapiApi::FunctionContext<NapiApi::Object> & ctx)642 void SceneJS::SetEnvironment(NapiApi::FunctionContext<NapiApi::Object>& ctx)
643 {
644     NapiApi::Object envObj = ctx.Arg<0>();
645     if (!envObj) {
646         return;
647     }
648     if (auto currentlySet = environmentJS_.GetObject()) {
649         if (envObj.StrictEqual(currentlySet)) { // setting the exactly the same environment. do nothing.
650             return;
651         }
652     }
653     environmentJS_ = NapiApi::StrongRef(envObj);
654 
655     SCENE_NS::IEnvironment::Ptr environment = GetNativeMeta<SCENE_NS::IEnvironment>(envObj);
656 
657     if (auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject())) {
658         auto rc = scene->RenderConfiguration()->GetValue();
659         if (rc) {
660             rc->Environment()->SetValue(environment);
661         }
662     }
663 }
664 
665 // resource factory
666 
GetResourceFactory(NapiApi::FunctionContext<> & ctx)667 napi_value SceneJS::GetResourceFactory(NapiApi::FunctionContext<>& ctx)
668 {
669     // just return this. as scene is the factory also.
670     return ctx.This().ToNapiValue();
671 }
CreateEnvironment(NapiApi::Object scene,NapiApi::Object argsIn)672 NapiApi::Object SceneJS::CreateEnvironment(NapiApi::Object scene, NapiApi::Object argsIn)
673 {
674     napi_env env = scene.GetEnv();
675     napi_value args[] = { scene.ToNapiValue(), argsIn.ToNapiValue() };
676     return NapiApi::Object(GetJSConstructor(env, "Environment"), BASE_NS::countof(args), args);
677 }
CreateEnvironment(NapiApi::FunctionContext<NapiApi::Object> & ctx)678 napi_value SceneJS::CreateEnvironment(NapiApi::FunctionContext<NapiApi::Object>& ctx)
679 {
680     struct Promise : public PromiseBase {
681         using PromiseBase::PromiseBase;
682         NapiApi::StrongRef this_;
683         NapiApi::StrongRef args_;
684         bool SetResult() override
685         {
686             auto* tro = static_cast<SceneJS*>(this_.GetObject().Native<TrueRootObject>());
687             if (tro == nullptr) {
688                 LOG_E("tro is nullptr");
689                 return false;
690             }
691             result_ = tro->CreateEnvironment(this_.GetObject(), args_.GetObject()).ToNapiValue();
692             return true;
693         };
694     };
695     Promise* promise = new Promise(ctx.Env());
696     auto jsPromise = promise->ToNapiValue();
697     promise->this_ = NapiApi::StrongRef(ctx.This());
698     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
699 
700     promise->SettleNow();
701     return jsPromise;
702 }
703 
CreateCamera(NapiApi::FunctionContext<NapiApi::Object> & ctx)704 napi_value SceneJS::CreateCamera(NapiApi::FunctionContext<NapiApi::Object>& ctx)
705 {
706     struct Promise : public PromiseBase {
707         using PromiseBase::PromiseBase;
708         NapiApi::StrongRef this_;
709         NapiApi::StrongRef args_;
710         bool SetResult() override
711         {
712             napi_value args[] = {
713                 this_.GetValue(), // scene..
714                 args_.GetValue()  // params.
715             };
716             result_ = NapiApi::Object(GetJSConstructor(env_, "Camera"), BASE_NS::countof(args), args).ToNapiValue();
717             return true;
718         };
719     };
720     Promise* promise = new Promise(ctx.Env());
721     auto jsPromise = promise->ToNapiValue();
722     promise->this_ = NapiApi::StrongRef(ctx.This());
723     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
724 
725     promise->SettleNow();
726     return jsPromise;
727 }
728 
CreateLight(NapiApi::FunctionContext<NapiApi::Object,uint32_t> & ctx)729 napi_value SceneJS::CreateLight(NapiApi::FunctionContext<NapiApi::Object, uint32_t>& ctx)
730 {
731     struct Promise : public PromiseBase {
732         using PromiseBase::PromiseBase;
733         NapiApi::StrongRef this_;
734         NapiApi::StrongRef args_;
735         uint32_t lightType_;
736         bool SetResult() override
737         {
738             napi_value args[] = {
739                 this_.GetValue(), // scene..
740                 args_.GetValue()  // params.
741             };
742             NapiApi::Function func;
743             switch (lightType_) {
744                 case BaseLight::DIRECTIONAL: {
745                     func = GetJSConstructor(env_, "DirectionalLight");
746                     break;
747                 }
748                 case BaseLight::POINT: {
749                     func = GetJSConstructor(env_, "PointLight");
750                     break;
751                 }
752                 case BaseLight::SPOT: {
753                     func = GetJSConstructor(env_, "SpotLight");
754                     break;
755                 }
756                 default:
757                     break;
758             }
759             if (!func) {
760                 return false;
761             }
762             result_ = NapiApi::Object(func, BASE_NS::countof(args), args).ToNapiValue();
763             return true;
764         };
765     };
766     Promise* promise = new Promise(ctx.Env());
767     auto jsPromise = promise->ToNapiValue();
768     promise->this_ = NapiApi::StrongRef(ctx.This());
769     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
770     promise->lightType_ = ctx.Arg<1>();
771 
772     promise->SettleNow();
773     return jsPromise;
774 }
775 
CreateNode(NapiApi::FunctionContext<NapiApi::Object> & ctx)776 napi_value SceneJS::CreateNode(NapiApi::FunctionContext<NapiApi::Object>& ctx)
777 {
778     struct Promise : public PromiseBase {
779         using PromiseBase::PromiseBase;
780         NapiApi::StrongRef this_;
781         NapiApi::StrongRef args_;
782         bool SetResult() override
783         {
784             napi_value args[] = {
785                 this_.GetValue(), // scene..
786                 args_.GetValue()  // params.
787             };
788             result_ = NapiApi::Object(GetJSConstructor(env_, "Node"), BASE_NS::countof(args), args).ToNapiValue();
789             return true;
790         };
791     };
792     Promise* promise = new Promise(ctx.Env());
793     auto jsPromise = promise->ToNapiValue();
794     promise->this_ = NapiApi::StrongRef(ctx.This());
795     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
796 
797     promise->SettleNow();
798     return jsPromise;
799 }
800 
CreateTextNode(NapiApi::FunctionContext<NapiApi::Object> & ctx)801 napi_value SceneJS::CreateTextNode(NapiApi::FunctionContext<NapiApi::Object>& ctx)
802 {
803     struct Promise : public PromiseBase {
804         using PromiseBase::PromiseBase;
805         NapiApi::StrongRef this_;
806         NapiApi::StrongRef args_;
807         bool SetResult() override
808         {
809             napi_value args[] = {
810                 this_.GetValue(), // scene..
811                 args_.GetValue()  // params.
812             };
813             result_ = NapiApi::Object(GetJSConstructor(env_, "TextNode"), BASE_NS::countof(args), args).ToNapiValue();
814             return true;
815         };
816     };
817     Promise* promise = new Promise(ctx.Env());
818     auto jsPromise = promise->ToNapiValue();
819     promise->this_ = NapiApi::StrongRef(ctx.This());
820     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
821 
822     promise->SettleNow();
823     return jsPromise;
824 }
825 
CreateMaterial(NapiApi::FunctionContext<NapiApi::Object,uint32_t> & ctx)826 napi_value SceneJS::CreateMaterial(NapiApi::FunctionContext<NapiApi::Object, uint32_t>& ctx)
827 {
828     struct Promise : public PromiseBase {
829         using PromiseBase::PromiseBase;
830         NapiApi::StrongRef this_;
831         NapiApi::StrongRef args_;
832         uint32_t type_;
833         BASE_NS::string name_;
834         SCENE_NS::IMaterial::Ptr material_;
835         SCENE_NS::IScene::Ptr scene_;
836         bool SetResult() override
837         {
838             napi_value args[] = {
839                 this_.GetValue(), // scene..
840                 args_.GetValue()  // params.
841             };
842 
843             if (type_ == BaseMaterial::SHADER) {
844                 META_NS::SetValue(material_->Type(), SCENE_NS::MaterialType::CUSTOM);
845             }
846 
847             MakeNativeObjectParam(env_, material_, BASE_NS::countof(args), args);
848             NapiApi::Object materialJS(GetJSConstructor(env_, "Material"), BASE_NS::countof(args), args);
849             result_ = materialJS.ToNapiValue();
850 
851             return true;
852         };
853     };
854     Promise* promise = new Promise(ctx.Env());
855     auto jsPromise = promise->ToNapiValue();
856     promise->this_ = NapiApi::StrongRef(ctx.This());
857     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
858     promise->type_ = ctx.Arg<1>();
859     NapiApi::Object parms = ctx.Arg<0>();
860     if (parms) {
861         promise->name_ = parms.Get<BASE_NS::string>("name");
862     }
863 
864     promise->scene_ = interface_pointer_cast<SCENE_NS::IScene>(GetNativeObject());
865     if (!promise->scene_) {
866         CORE_LOG_E("promise->scene_ is null.");
867         promise->Reject();
868         return jsPromise;
869     }
870 
871     // create an engine task and complete it there..
872     auto fun = [promise]() {
873         promise->material_ =
874             promise->scene_->CreateObject<SCENE_NS::IMaterial>(SCENE_NS::ClassId::Material).GetResult();
875         promise->SettleLater();
876         return false;
877     };
878 
879     META_NS::GetTaskQueueRegistry()
880         .GetTaskQueue(ENGINE_THREAD)
881         ->AddTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(fun)));
882 
883     return jsPromise;
884 }
885 
CreateScene(NapiApi::FunctionContext<> & ctx)886 napi_value SceneJS::CreateScene(NapiApi::FunctionContext<>& ctx)
887 {
888     return Load(ctx);
889 }
890 
ImportNode(NapiApi::FunctionContext<BASE_NS::string,NapiApi::Object,NapiApi::Object> & ctx)891 napi_value SceneJS::ImportNode(NapiApi::FunctionContext<BASE_NS::string, NapiApi::Object, NapiApi::Object>& ctx)
892 {
893     BASE_NS::string name = ctx.Arg<0>();
894     NapiApi::Object nnode = ctx.Arg<1>();
895     NapiApi::Object nparent = ctx.Arg<2>();
896     auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject());
897     if (!nnode || !scene) {
898         return ctx.GetNull();
899     }
900     SCENE_NS::INode::Ptr node = GetNativeMeta<SCENE_NS::INode>(nnode);
901     SCENE_NS::INode::Ptr parent;
902     if (nparent) {
903         parent = GetNativeMeta<SCENE_NS::INode>(nparent);
904     } else {
905         parent = scene->GetRootNode().GetResult();
906     }
907 
908     if (auto import = interface_cast<SCENE_NS::INodeImport>(parent)) {
909         auto result = import->ImportChild(node).GetResult();
910         if (!name.empty()) {
911             result->SetName(name).Wait();
912         }
913         auto obj = interface_pointer_cast<META_NS::IObject>(result);
914         if (auto cached = FetchJsObj(obj)) {
915             // always return the same js object.
916             return cached.ToNapiValue();
917         }
918 
919         NapiApi::StrongRef sceneRef { ctx.This() };
920         NapiApi::Object argJS(ctx.GetEnv());
921         napi_value args[] = { sceneRef.GetObject().ToNapiValue(), argJS.ToNapiValue() };
922 
923         return CreateFromNativeInstance(
924             ctx.GetEnv(), obj, false /*these are owned by the scene*/, BASE_NS::countof(args), args)
925             .ToNapiValue();
926     }
927     return ctx.GetNull();
928 }
929 
ImportScene(NapiApi::FunctionContext<BASE_NS::string,NapiApi::Object,NapiApi::Object> & ctx)930 napi_value SceneJS::ImportScene(NapiApi::FunctionContext<BASE_NS::string, NapiApi::Object, NapiApi::Object>& ctx)
931 {
932     BASE_NS::string name = ctx.Arg<0>();
933     NapiApi::Object nextScene = ctx.Arg<1>();
934     NapiApi::Object nparent = ctx.Arg<2>();
935     auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject());
936     if (!nextScene || !scene) {
937         return ctx.GetNull();
938     }
939     SCENE_NS::IScene::Ptr extScene = GetNativeMeta<SCENE_NS::IScene>(nextScene);
940     SCENE_NS::INode::Ptr parent;
941     if (nparent) {
942         parent = GetNativeMeta<SCENE_NS::INode>(nparent);
943     } else {
944         parent = scene->GetRootNode().GetResult();
945     }
946 
947     if (auto import = interface_cast<SCENE_NS::INodeImport>(parent)) {
948         auto result = import->ImportChildScene(extScene, name).GetResult();
949         auto obj = interface_pointer_cast<META_NS::IObject>(result);
950         if (auto cached = FetchJsObj(obj)) {
951             // always return the same js object.
952             return cached.ToNapiValue();
953         }
954 
955         NapiApi::StrongRef sceneRef { ctx.This() };
956         NapiApi::Object argJS(ctx.GetEnv());
957         napi_value args[] = { sceneRef.GetObject().ToNapiValue(), argJS.ToNapiValue() };
958 
959         return CreateFromNativeInstance(
960             ctx.GetEnv(), obj, false /*these are owned by the scene*/, BASE_NS::countof(args), args)
961             .ToNapiValue();
962     }
963     return ctx.GetNull();
964 }
965 
GetRenderMode(NapiApi::FunctionContext<> & ctx)966 napi_value SceneJS::GetRenderMode(NapiApi::FunctionContext<>& ctx)
967 {
968     auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject());
969     if (!scene) {
970         return ctx.GetUndefined();
971     }
972     return ctx.GetNumber(uint32_t(scene->GetRenderMode().GetResult()));
973 }
974 
SetRenderMode(NapiApi::FunctionContext<uint32_t> & ctx)975 void SceneJS::SetRenderMode(NapiApi::FunctionContext<uint32_t>& ctx)
976 {
977     auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject());
978     if (!scene) {
979         return;
980     }
981     uint32_t v = ctx.Arg<0>();
982     if (v >= static_cast<uint32_t>(SCENE_NS::RenderMode::IF_DIRTY) &&
983         v <= static_cast<uint32_t>(SCENE_NS::RenderMode::MANUAL)) {
984         scene->SetRenderMode(static_cast<SCENE_NS::RenderMode>(v)).Wait();
985     }
986 }
987 
CreateMeshResource(NapiApi::FunctionContext<NapiApi::Object,NapiApi::Object> & ctx)988 napi_value SceneJS::CreateMeshResource(NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object>& ctx)
989 {
990     struct Promise : public PromiseBase {
991         using PromiseBase::PromiseBase;
992         NapiApi::StrongRef this_;
993         NapiApi::StrongRef resourceParams_;
994         bool SetResult() override
995         {
996             napi_value args[] = { this_.GetValue(), resourceParams_.GetValue() };
997             auto meshResource = NapiApi::Object(GetJSConstructor(env_, "MeshResource"), BASE_NS::countof(args), args);
998             result_ = meshResource.ToNapiValue();
999             return (bool)result_;
1000         }
1001     };
1002     auto promise = new Promise(ctx.Env());
1003     auto jsPromise = promise->ToNapiValue();
1004     promise->this_ = NapiApi::StrongRef(ctx.This());
1005     NapiApi::Object resourceParams = ctx.Arg<0>();
1006     promise->resourceParams_ = NapiApi::StrongRef(resourceParams);
1007 
1008     auto geometry = GeometryDefinition::GeometryDefinition::FromJs(ctx.Arg<1>());
1009     if (!geometry) {
1010         promise->Reject();
1011         return jsPromise;
1012     }
1013     napi_value geometryNapiValue;
1014     // Piggyback the native geometry definition inside the resource param. Need to ditch smart pointers for the ride.
1015     napi_create_external(ctx.Env(), geometry.release(), nullptr, nullptr, &geometryNapiValue);
1016     resourceParams.Set("GeometryDefinition", geometryNapiValue);
1017 
1018     auto func = [promise]() {
1019         promise->SettleLater();
1020         return false;
1021     };
1022     auto task = META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(func));
1023     META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddTask(task);
1024 
1025     return jsPromise;
1026 }
1027 
CreateGeometry(NapiApi::FunctionContext<NapiApi::Object,NapiApi::Object> & ctx)1028 napi_value SceneJS::CreateGeometry(NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object>& ctx)
1029 {
1030     struct Promise : public PromiseBase {
1031         using PromiseBase::PromiseBase;
1032         NapiApi::StrongRef this_;
1033         NapiApi::StrongRef nodeParams_;
1034         NapiApi::StrongRef meshResource_;
1035         bool SetResult() override
1036         {
1037             napi_value args[] = { this_.GetValue(), nodeParams_.GetValue(), meshResource_.GetValue() };
1038             result_ = NapiApi::Object(GetJSConstructor(env_, "Geometry"), BASE_NS::countof(args), args).ToNapiValue();
1039             return (bool)result_;
1040         }
1041     };
1042     auto promise = new Promise(ctx.Env());
1043     auto jsPromise = promise->ToNapiValue();
1044     promise->this_ = NapiApi::StrongRef(ctx.This());
1045     promise->nodeParams_ = NapiApi::StrongRef(ctx.Arg<0>());
1046     promise->meshResource_ = NapiApi::StrongRef(ctx.Arg<1>());
1047 
1048     auto func = [promise]() {
1049         promise->SettleLater();
1050         return false;
1051     };
1052     auto task = META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(func));
1053     META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddTask(task);
1054 
1055     return jsPromise;
1056 }
1057 
CreateShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)1058 napi_value SceneJS::CreateShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
1059 {
1060     struct Promise : public PromiseBase {
1061         using PromiseBase::PromiseBase;
1062         NapiApi::StrongRef this_;
1063         NapiApi::StrongRef args_;
1064         BASE_NS::string uri_;
1065         BASE_NS::string name_;
1066         SCENE_NS::IShader::Ptr shader_;
1067         SCENE_NS::IScene::Ptr scene_;
1068         bool SetResult() override
1069         {
1070             napi_value args[] = {
1071                 this_.GetValue(), // scene..
1072                 args_.GetValue()  // params.
1073             };
1074             NapiApi::Object parms(env_, args[1]);
1075 
1076             napi_value null;
1077             napi_get_null(env_, &null);
1078             parms.Set("Material", null); // not bound to anything...
1079 
1080             MakeNativeObjectParam(env_, shader_, BASE_NS::countof(args), args);
1081             NapiApi::Object shaderJS(GetJSConstructor(env_, "Shader"), BASE_NS::countof(args), args);
1082             result_ = shaderJS.ToNapiValue();
1083 
1084             return true;
1085         };
1086     };
1087     Promise* promise = new Promise(ctx.Env());
1088     auto jsPromise = promise->ToNapiValue();
1089     promise->this_ = NapiApi::StrongRef(ctx.This());
1090     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
1091     promise->scene_ = interface_pointer_cast<SCENE_NS::IScene>(GetNativeObject());
1092     if (!promise->scene_) {
1093         CORE_LOG_E("promise->scene_ is null.");
1094         promise->Reject();
1095         return jsPromise;
1096     }
1097 
1098     NapiApi::Object parms = ctx.Arg<0>();
1099     if (parms) {
1100         promise->name_ = parms.Get<BASE_NS::string>("name");
1101         promise->uri_ = FetchResourceOrUri(ctx.Env(), parms.Get("uri"));
1102     }
1103 
1104     auto fun = [promise]() {
1105         promise->shader_ = META_NS::GetObjectRegistry().Create<SCENE_NS::IShader>(SCENE_NS::ClassId::Shader);
1106         if (!promise->shader_ || !promise->shader_->LoadShader(promise->scene_, promise->uri_).GetResult()) {
1107             LOG_W("Failed to load shader: %s", promise->uri_.c_str());
1108         }
1109         promise->SettleLater();
1110         return false;
1111     };
1112 
1113     META_NS::GetTaskQueueRegistry()
1114         .GetTaskQueue(ENGINE_THREAD)
1115         ->AddTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(fun)));
1116 
1117     return jsPromise;
1118 }
1119 
StoreBitmap(BASE_NS::string_view uri,SCENE_NS::IBitmap::Ptr bitmap)1120 void SceneJS::StoreBitmap(BASE_NS::string_view uri, SCENE_NS::IBitmap::Ptr bitmap)
1121 {
1122     CORE_NS::UniqueLock lock(mutex_);
1123     if (bitmap) {
1124         bitmaps_[uri] = bitmap;
1125     } else {
1126         // setting null. releases.
1127         bitmaps_.erase(uri);
1128     }
1129 }
FetchBitmap(BASE_NS::string_view uri)1130 SCENE_NS::IBitmap::Ptr SceneJS::FetchBitmap(BASE_NS::string_view uri)
1131 {
1132     CORE_NS::UniqueLock lock(mutex_);
1133     auto it = bitmaps_.find(uri);
1134     if (it != bitmaps_.end()) {
1135         return it->second;
1136     }
1137     return {};
1138 }
1139 
CreateImage(NapiApi::FunctionContext<NapiApi::Object> & ctx)1140 napi_value SceneJS::CreateImage(NapiApi::FunctionContext<NapiApi::Object>& ctx)
1141 {
1142     using namespace RENDER_NS;
1143 
1144     struct Promise : public PromiseBase {
1145         using PromiseBase::PromiseBase;
1146         NapiApi::StrongRef this_;
1147         NapiApi::StrongRef args_;
1148         BASE_NS::shared_ptr<IRenderContext> renderContext_;
1149         BASE_NS::string name_;
1150         BASE_NS::string uri_;
1151         SCENE_NS::IBitmap::Ptr bitmap_;
1152         RenderHandleReference imageHandle_;
1153         SceneJS* owner_;
1154         CORE_NS::IImageLoaderManager::LoadResult imageLoadResult_;
1155         bool cached_ { false };
1156         SCENE_NS::IScene::Ptr scene_;
1157         bool SetResult() override
1158         {
1159             if (!bitmap_) {
1160                 // return the error string..
1161                 NapiApi::Env e(env_);
1162                 result_ = e.GetString(imageLoadResult_.error);
1163                 return false;
1164             }
1165             if (cached_) {
1166                 auto obj = interface_pointer_cast<META_NS::IObject>(bitmap_);
1167                 result_ = FetchJsObj(obj).ToNapiValue();
1168             } else {
1169                 // create the jsobject if we don't have one.
1170                 napi_value args[] = {
1171                     this_.GetValue(), // scene..
1172                     args_.GetValue()  // params.
1173                 };
1174                 MakeNativeObjectParam(env_, bitmap_, BASE_NS::countof(args), args);
1175                 owner_->StoreBitmap(uri_, BASE_NS::move(bitmap_));
1176                 NapiApi::Object imageJS(GetJSConstructor(env_, "Image"), BASE_NS::countof(args), args);
1177                 result_ = imageJS.ToNapiValue();
1178             }
1179             return true;
1180         };
1181     };
1182     Promise* promise = new Promise(ctx.Env());
1183     auto jsPromise = promise->ToNapiValue();
1184     promise->owner_ = this;
1185     promise->this_ = NapiApi::StrongRef(ctx.This());
1186     promise->args_ = NapiApi::StrongRef(ctx.Arg<0>());
1187     promise->scene_ = interface_pointer_cast<SCENE_NS::IScene>(GetNativeObject());
1188     if (!promise->scene_) {
1189         CORE_LOG_E("promise->scene_ is null.");
1190         promise->Reject();
1191         return jsPromise;
1192     }
1193 
1194     NapiApi::Object args = ctx.Arg<0>();
1195     if (args) {
1196         if (auto n = args.Get<BASE_NS::string>("name"); n.IsDefined()) {
1197             promise->name_ = n;
1198         }
1199         promise->uri_ = FetchResourceOrUri(ctx.Env(), args.Get("uri"));
1200     }
1201 
1202     if (auto bitmap = FetchBitmap(promise->uri_)) {
1203         // no aliasing.. so the returned bitmaps name is.. the old one.
1204         // *fix*
1205         // oh we have it already, no need to do anything in engine side.
1206         promise->cached_ = true;
1207         promise->bitmap_ = bitmap;
1208         auto jsPromise = promise->ToNapiValue();
1209         promise->SettleNow();
1210         return jsPromise;
1211     }
1212 
1213     auto& obr = META_NS::GetObjectRegistry();
1214     auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
1215     promise->renderContext_ =
1216         interface_pointer_cast<IRenderContext>(doc->GetProperty<IntfPtr>("RenderContext")->GetValue());
1217 
1218     // create an IO task (to load the cpu data)
1219     auto fun = [promise]() -> META_NS::IAny::Ptr {
1220         uint32_t imageLoaderFlags = CORE_NS::IImageLoaderManager::IMAGE_LOADER_GENERATE_MIPS;
1221         auto& imageLoaderMgr = promise->renderContext_->GetEngine().GetImageLoaderManager();
1222         promise->imageLoadResult_ = imageLoaderMgr.LoadImage(promise->uri_, imageLoaderFlags);
1223         return {};
1224     };
1225 
1226     // create final engine task (to create gpu resource)
1227     auto fun2 = [promise](const META_NS::IAny::Ptr&) -> META_NS::IAny::Ptr {
1228         if (!promise->imageLoadResult_.success) {
1229             LOG_E("Could not load image asset: %s", promise->imageLoadResult_.error);
1230             promise->bitmap_ = nullptr;
1231         } else {
1232             auto& gpuResourceMgr = promise->renderContext_->GetDevice().GetGpuResourceManager();
1233             RenderHandleReference imageHandle {};
1234             GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(promise->imageLoadResult_.image->GetImageDesc());
1235             gpuDesc.usageFlags = CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT;
1236             if (gpuDesc.engineCreationFlags & EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS) {
1237                 gpuDesc.usageFlags |= CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
1238             }
1239             gpuDesc.memoryPropertyFlags = CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
1240             promise->imageHandle_ =
1241                 gpuResourceMgr.Create(promise->uri_, gpuDesc, std::move(promise->imageLoadResult_.image));
1242             promise->bitmap_ = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(SCENE_NS::ClassId::Bitmap);
1243             if (auto m = interface_cast<META_NS::IMetadata>(promise->bitmap_)) {
1244                 auto uri = META_NS::ConstructProperty<BASE_NS::string>("Uri", promise->uri_);
1245                 m->AddProperty(uri);
1246             }
1247             if (auto i = interface_cast<SCENE_NS::IRenderResource>(promise->bitmap_)) {
1248                 i->SetRenderHandle(promise->scene_->GetInternalScene(), promise->imageHandle_);
1249             }
1250         }
1251         promise->SettleLater();
1252         return {};
1253     };
1254 
1255     // execute first step in io thread..
1256     // second in engine thread.
1257     // and the final in js thread (with threadsafefunction)
1258     auto ioqueue = META_NS::GetTaskQueueRegistry().GetTaskQueue(IO_QUEUE);
1259     auto enginequeue = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD);
1260     ioqueue->AddWaitableTask(META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>(BASE_NS::move(fun)))
1261         ->Then(META_NS::MakeCallback<META_NS::IFutureContinuation>(BASE_NS::move(fun2)), enginequeue);
1262 
1263     return jsPromise;
1264 }
GetAnimations(NapiApi::FunctionContext<> & ctx)1265 napi_value SceneJS::GetAnimations(NapiApi::FunctionContext<>& ctx)
1266 {
1267     auto scene = interface_pointer_cast<SCENE_NS::IScene>(GetThisNativeObject(ctx));
1268     if (!scene) {
1269         return ctx.GetUndefined();
1270     }
1271 
1272     BASE_NS::vector<META_NS::IAnimation::Ptr> animRes;
1273     ExecSyncTask([scene, &animRes]() {
1274         animRes = scene->GetAnimations().GetResult();
1275         return META_NS::IAny::Ptr {};
1276     });
1277 
1278     napi_value tmp;
1279     auto status = napi_create_array_with_length(ctx.Env(), animRes.size(), &tmp);
1280     size_t i = 0;
1281     napi_value args[] = { ctx.This().ToNapiValue(), NapiApi::Object(ctx.Env()).ToNapiValue() };
1282     for (const auto& node : animRes) {
1283         auto val = CreateFromNativeInstance(
1284             ctx.Env(), interface_pointer_cast<META_NS::IObject>(node), true, BASE_NS::countof(args), args);
1285         status = napi_set_element(ctx.Env(), tmp, i++, val.ToNapiValue());
1286     }
1287 
1288     return tmp;
1289 }
1290 
DisposeHook(uintptr_t token,NapiApi::Object obj)1291 void SceneJS::DisposeHook(uintptr_t token, NapiApi::Object obj)
1292 {
1293     disposables_[token] = { obj };
1294 }
ReleaseDispose(uintptr_t token)1295 void SceneJS::ReleaseDispose(uintptr_t token)
1296 {
1297     auto it = disposables_.find(token);
1298     if (it != disposables_.end()) {
1299         it->second.Reset();
1300         disposables_.erase(it->first);
1301     }
1302 }
1303 
StrongDisposeHook(uintptr_t token,NapiApi::Object obj)1304 void SceneJS::StrongDisposeHook(uintptr_t token, NapiApi::Object obj)
1305 {
1306     strongDisposables_[token] = NapiApi::StrongRef(obj);
1307 }
ReleaseStrongDispose(uintptr_t token)1308 void SceneJS::ReleaseStrongDispose(uintptr_t token)
1309 {
1310     auto it = strongDisposables_.find(token);
1311     if (it != strongDisposables_.end()) {
1312         it->second.Reset();
1313         strongDisposables_.erase(it->first);
1314     }
1315 }
1316 
1317 #ifdef __OHOS_PLATFORM__
1318 // This will circumvent the broken napi_set_instance_data and napi_get_instance_data implementations
1319 // on ohos platform
1320 static void* g_instanceData = nullptr;
1321 
SetInstanceData(napi_env env,void * data,napi_finalize finalizeCb,void * finalizeHint)1322 napi_status SetInstanceData(napi_env env, void* data, napi_finalize finalizeCb, void* finalizeHint)
1323 {
1324     g_instanceData = data;
1325     return napi_ok;
1326 }
1327 
GetInstanceData(napi_env env,void ** data)1328 napi_status GetInstanceData(napi_env env, void** data)
1329 {
1330     if (data) {
1331         *data = g_instanceData;
1332     }
1333 
1334     return napi_ok;
1335 }
1336 
1337 #else // __OHOS_PLATFORM__
1338 
SetInstanceData(napi_env env,void * data,napi_finalize finalizeCb,void * finalizeHint)1339 napi_status SetInstanceData(napi_env env, void* data, napi_finalize finalizeCb, void* finalizeHint)
1340 {
1341     return napi_set_instance_data(env, data, finalizeCb, finalizeHint);
1342 }
1343 
GetInstanceData(napi_env env,void ** data)1344 napi_status GetInstanceData(napi_env env, void** data)
1345 {
1346     return napi_get_instance_data(env, data);
1347 }
1348 
1349 #endif // __OHOS_PLATFORM__
1350