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 "EnvironmentJS.h"
16 
17 #include <meta/api/make_callback.h>
18 #include <meta/interface/intf_task_queue.h>
19 #include <meta/interface/intf_task_queue_registry.h>
20 #include <meta/interface/property/property_events.h>
21 #include <scene_plugin/api/camera.h> // for the classid..
22 #include <scene_plugin/api/node_uid.h>
23 #include <scene_plugin/interface/intf_ecs_scene.h>
24 #include <scene_plugin/interface/intf_node.h>
25 #include <scene_plugin/interface/intf_scene.h>
26 
27 #include <render/intf_render_context.h>
28 
29 #include "SceneJS.h"
30 using namespace SCENE_NS;
31 
Init(napi_env env,napi_value exports)32 void EnvironmentJS::Init(napi_env env, napi_value exports)
33 {
34     using namespace NapiApi;
35 
36     BASE_NS::vector<napi_property_descriptor> node_props;
37     SceneResourceImpl::GetPropertyDescs(node_props);
38     // clang-format off
39 
40     node_props.emplace_back(GetSetProperty<uint32_t, EnvironmentJS, &EnvironmentJS::GetBackgroundType,
41         &EnvironmentJS::SetBackgroundType>("backgroundType"));
42     node_props.emplace_back(GetSetProperty<Object, EnvironmentJS, &EnvironmentJS::GetEnvironmentImage,
43         &EnvironmentJS::SetEnvironmentImage>("environmentImage"));
44     node_props.emplace_back(GetSetProperty<Object, EnvironmentJS, &EnvironmentJS::GetRadianceImage,
45         &EnvironmentJS::SetRadianceImage>("radianceImage"));
46     node_props.emplace_back(GetSetProperty<NapiApi::Array, EnvironmentJS, &EnvironmentJS::GetIrradianceCoefficients,
47         &EnvironmentJS::SetIrradianceCoefficients>("irradianceCoefficients"));
48     node_props.emplace_back(GetSetProperty<Object, EnvironmentJS, &EnvironmentJS::GetIndirectDiffuseFactor,
49         &EnvironmentJS::SetIndirectDiffuseFactor>("indirectDiffuseFactor"));
50     node_props.emplace_back(GetSetProperty<Object, EnvironmentJS, &EnvironmentJS::GetIndirectSpecularFactor,
51         &EnvironmentJS::SetIndirectSpecularFactor>("indirectSpecularFactor"));
52     node_props.emplace_back(GetSetProperty<Object, EnvironmentJS, &EnvironmentJS::GetEnvironmentMapFactor,
53         &EnvironmentJS::SetEnvironmentMapFactor>("environmentMapFactor"));
54 
55     // clang-format on
56 
57     napi_value func;
58     auto status = napi_define_class(env, "Environment", NAPI_AUTO_LENGTH, BaseObject::ctor<EnvironmentJS>(), nullptr,
59         node_props.size(), node_props.data(), &func);
60 
61     NapiApi::MyInstanceState* mis;
62     napi_get_instance_data(env, (void**)&mis);
63     mis->StoreCtor("Environment", func);
64 
65     NapiApi::Object exp(env, exports);
66 
67     napi_value eType;
68     napi_value v;
69     napi_create_object(env, &eType);
70 #define DECL_ENUM(enu, x)                                      \
71     napi_create_uint32(env, EnvironmentBackgroundType::x, &v); \
72     napi_set_named_property(env, enu, #x, v)
73 
74     DECL_ENUM(eType, BACKGROUND_NONE);
75     DECL_ENUM(eType, BACKGROUND_IMAGE);
76     DECL_ENUM(eType, BACKGROUND_CUBEMAP);
77     DECL_ENUM(eType, BACKGROUND_EQUIRECTANGULAR);
78 #undef DECL_ENUM
79     exp.Set("EnvironmentBackgroundType", eType);
80 }
81 
Dispose(NapiApi::FunctionContext<> & ctx)82 napi_value EnvironmentJS::Dispose(NapiApi::FunctionContext<>& ctx)
83 {
84     LOG_F("EnvironmentJS::Dispose");
85     DisposeNative();
86     return {};
87 }
DisposeNative()88 void EnvironmentJS::DisposeNative()
89 {
90     if (!disposed_) {
91         CORE_LOG_F("EnvironmentJS::DisposeNative");
92         disposed_ = true;
93         NapiApi::Object obj = scene_.GetObject();
94         auto* tro = obj.Native<TrueRootObject>();
95         if (tro) {
96             SceneJS* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
97             if (sceneJS) {
98                 sceneJS->ReleaseStrongDispose((uintptr_t)&scene_);
99             }
100         }
101 
102         diffuseFactor_.reset();
103         specularFactor_.reset();
104         environmentFactor_.reset();
105         if (auto env = interface_pointer_cast<IEnvironment>(GetNativeObject())) {
106             // reset the native object refs
107             SetNativeObject(nullptr, false);
108             SetNativeObject(nullptr, true);
109             diffuseFactor_.reset();
110             specularFactor_.reset();
111             environmentFactor_.reset();
112 
113             NapiApi::Object sceneJS = scene_.GetObject();
114             if (sceneJS) {
115                 napi_value null;
116                 napi_get_null(sceneJS.GetEnv(), &null);
117                 sceneJS.Set("environment", null);
118 
119                 scene_.Reset();
120                 auto* tro = sceneJS.Native<TrueRootObject>();
121                 IScene::Ptr scene = interface_pointer_cast<IScene>(tro->GetNativeObject());
122                 ExecSyncTask([s = BASE_NS::move(scene), e = BASE_NS::move(env)]() {
123                     auto en = interface_pointer_cast<SCENE_NS::INode>(e);
124                     s->ReleaseNode(en);
125                     en.reset();
126                     return META_NS::IAny::Ptr {};
127                 });
128             }
129         }
130     }
131     scene_.Reset();
132 }
GetInstanceImpl(uint32_t id)133 void* EnvironmentJS::GetInstanceImpl(uint32_t id)
134 {
135     if (id == EnvironmentJS::ID) {
136         return this;
137     }
138     return SceneResourceImpl::GetInstanceImpl(id);
139 }
Finalize(napi_env env)140 void EnvironmentJS::Finalize(napi_env env)
141 {
142     // hmm.. do i need to do something BEFORE the object gets deleted..
143     DisposeNative();
144     BaseObject<EnvironmentJS>::Finalize(env);
145 }
146 
EnvironmentJS(napi_env e,napi_callback_info i)147 EnvironmentJS::EnvironmentJS(napi_env e, napi_callback_info i)
148     : BaseObject<EnvironmentJS>(e, i), SceneResourceImpl(SceneResourceImpl::ENVIRONMENT)
149 {
150     LOG_F("EnvironmentJS ++");
151     NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
152     if (!fromJs) {
153         // no arguments. so internal create.
154         // expecting caller to finish
155         return;
156     }
157 
158     scene_ = { fromJs, fromJs.Arg<0>() };
159     if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
160         CORE_LOG_F("INVALID SCENE!");
161     }
162 
163     NapiApi::Object meJs(e, fromJs.This());
164     auto* tro = scene_.GetObject().Native<TrueRootObject>();
165     auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
166     sceneJS->StrongDisposeHook((uintptr_t)&scene_, meJs);
167 
168     IScene::Ptr scene = interface_pointer_cast<IScene>(tro->GetNativeObject());
169 
170     NapiApi::Value<BASE_NS::string> name;
171     NapiApi::Object args = fromJs.Arg<1>();
172     if (auto prm = args.Get("name")) {
173         name = NapiApi::Value<BASE_NS::string>(e, prm);
174     }
175 
176     BASE_NS::string nameS = name;
177     if (nameS.empty()) {
178         // create "unique" name
179         nameS = BASE_NS::to_string((uint64_t)this);
180     }
181     IEnvironment::Ptr env = GetNativeMeta<IEnvironment>(meJs);
182     // Construct native object (if needed)
183 
184     if (!env) {
185         ExecSyncTask([&env, scene, nameS]() {
186             BASE_NS::string_view n = nameS; /*nodepath actually*/
187             env = scene->CreateNode<SCENE_NS::IEnvironment>(nameS);
188             return META_NS::IAny::Ptr {};
189         });
190     }
191 
192     // process constructor args
193     // weak ref, due to being owned by the scene.
194     SetNativeObject(interface_pointer_cast<META_NS::IObject>(env), false);
195     StoreJsObj(interface_pointer_cast<META_NS::IObject>(env), meJs);
196     env.reset();
197 
198     if (name) {
199         // set the name of the object. if we were given one
200         meJs.Set("name", name);
201     }
202 }
203 
~EnvironmentJS()204 EnvironmentJS::~EnvironmentJS()
205 {
206     LOG_F("EnvironmentJS --");
207     DisposeNative();
208     if (!GetNativeObject()) {
209         return;
210     }
211 }
212 
GetBackgroundType(NapiApi::FunctionContext<> & ctx)213 napi_value EnvironmentJS::GetBackgroundType(NapiApi::FunctionContext<>& ctx)
214 {
215     uint32_t typeI = 0;
216     if (auto env = interface_cast<IEnvironment>(GetNativeObject())) {
217         ExecSyncTask([env, &typeI]() {
218             typeI = env->Background()->GetValue();
219             return META_NS::IAny::Ptr {};
220         });
221     }
222     return NapiApi::Value(ctx, (uint32_t)typeI);
223 }
224 
SetBackgroundType(NapiApi::FunctionContext<uint32_t> & ctx)225 void EnvironmentJS::SetBackgroundType(NapiApi::FunctionContext<uint32_t>& ctx)
226 {
227     if (auto env = interface_cast<IEnvironment>(GetNativeObject())) {
228         uint32_t typeI = ctx.Arg<0>();
229         auto typeE = static_cast<EnvironmentBackgroundType>(typeI);
230         IEnvironment::BackgroundType type;
231         switch (typeE) {
232             case EnvironmentBackgroundType::BACKGROUND_NONE:
233                 type = IEnvironment::BackgroundType::NONE;
234                 break;
235             case EnvironmentBackgroundType::BACKGROUND_IMAGE:
236                 type = IEnvironment::BackgroundType::IMAGE;
237                 break;
238             case EnvironmentBackgroundType::BACKGROUND_CUBEMAP:
239                 type = IEnvironment::BackgroundType::CUBEMAP;
240                 break;
241             case EnvironmentBackgroundType::BACKGROUND_EQUIRECTANGULAR:
242                 type = IEnvironment::BackgroundType::EQUIRECTANGULAR;
243                 break;
244             default:
245                 type = IEnvironment::BackgroundType::NONE;
246                 break;
247         }
248         ExecSyncTask([env, &type]() {
249             env->Background()->SetValue(type);
250             return META_NS::IAny::Ptr {};
251         });
252     }
253 }
GetEnvironmentImage(NapiApi::FunctionContext<> & ctx)254 napi_value EnvironmentJS::GetEnvironmentImage(NapiApi::FunctionContext<>& ctx)
255 {
256     if (auto environment = interface_cast<SCENE_NS::IEnvironment>(GetNativeObject())) {
257         SCENE_NS::IBitmap::Ptr image;
258         ExecSyncTask([environment, &image]() {
259             image = environment->EnvironmentImage()->GetValue();
260             return META_NS::IAny::Ptr {};
261         });
262         auto obj = interface_pointer_cast<META_NS::IObject>(image);
263 
264         if (auto cached = FetchJsObj(obj)) {
265             return cached;
266         }
267 
268         napi_value args[] = { scene_.GetValue(), NapiApi::Object(ctx) };
269         return CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args);
270     }
271     return ctx.GetNull();
272 }
273 
SetEnvironmentImage(NapiApi::FunctionContext<NapiApi::Object> & ctx)274 void EnvironmentJS::SetEnvironmentImage(NapiApi::FunctionContext<NapiApi::Object>& ctx)
275 {
276     NapiApi::Object imageJS = ctx.Arg<0>();
277     SCENE_NS::IBitmap::Ptr image;
278     if (auto nat = imageJS.Native<TrueRootObject>()) {
279         image = interface_pointer_cast<SCENE_NS::IBitmap>(nat->GetNativeObject());
280     }
281     if (auto environment = interface_cast<SCENE_NS::IEnvironment>(GetNativeObject())) {
282         ExecSyncTask([environment, image]() {
283             environment->EnvironmentImage()->SetValue(image);
284             return META_NS::IAny::Ptr {};
285         });
286     }
287 }
288 
GetRadianceImage(NapiApi::FunctionContext<> & ctx)289 napi_value EnvironmentJS::GetRadianceImage(NapiApi::FunctionContext<>& ctx)
290 {
291     if (auto environment = interface_cast<SCENE_NS::IEnvironment>(GetNativeObject())) {
292         SCENE_NS::IBitmap::Ptr image;
293         ExecSyncTask([environment, &image]() {
294             image = environment->RadianceImage()->GetValue();
295             return META_NS::IAny::Ptr {};
296         });
297         auto obj = interface_pointer_cast<META_NS::IObject>(image);
298 
299         if (auto cached = FetchJsObj(obj)) {
300             return cached;
301         }
302 
303         napi_value args[] = { scene_.GetValue(), NapiApi::Object(ctx) };
304         return CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args);
305     }
306     return ctx.GetNull();
307 }
308 
SetRadianceImage(NapiApi::FunctionContext<NapiApi::Object> & ctx)309 void EnvironmentJS::SetRadianceImage(NapiApi::FunctionContext<NapiApi::Object>& ctx)
310 {
311     NapiApi::Object imageJS = ctx.Arg<0>();
312     SCENE_NS::IBitmap::Ptr image;
313     if (auto nat = imageJS.Native<TrueRootObject>()) {
314         image = interface_pointer_cast<SCENE_NS::IBitmap>(nat->GetNativeObject());
315     }
316     if (auto environment = interface_cast<SCENE_NS::IEnvironment>(GetNativeObject())) {
317         ExecSyncTask([environment, image]() {
318             environment->RadianceImage()->SetValue(image);
319             return META_NS::IAny::Ptr {};
320         });
321     }
322 }
GetIrradianceCoefficients(NapiApi::FunctionContext<> & ctx)323 napi_value EnvironmentJS::GetIrradianceCoefficients(NapiApi::FunctionContext<>& ctx)
324 {
325     BASE_NS::vector<BASE_NS::Math::Vec3> coeffs;
326     if (auto environment = interface_cast<SCENE_NS::IEnvironment>(GetNativeObject())) {
327         ExecSyncTask([environment, &coeffs]() {
328             coeffs = environment->IrradianceCoefficients()->GetValue();
329             return META_NS::IAny::Ptr {};
330         });
331     }
332     NapiApi::Array res(ctx, 9); // array size 9
333     size_t index = 0;
334     for (auto& v : coeffs) {
335         NapiApi::Object vec(ctx);
336         vec.Set("x", NapiApi::Value<float>(ctx, v.x));
337         vec.Set("y", NapiApi::Value<float>(ctx, v.y));
338         vec.Set("z", NapiApi::Value<float>(ctx, v.z));
339         res.Set(index++, vec);
340     }
341     return res;
342 }
SetIrradianceCoefficients(NapiApi::FunctionContext<NapiApi::Array> & ctx)343 void EnvironmentJS::SetIrradianceCoefficients(NapiApi::FunctionContext<NapiApi::Array>& ctx)
344 {
345     NapiApi::Array coeffJS = ctx.Arg<0>();
346     if (coeffJS.Count() != 9) { // array size 9
347         // not enough elements in array
348         return;
349     }
350     BASE_NS::vector<BASE_NS::Math::Vec3> coeffs;
351     for (auto i = 0; i < coeffJS.Count(); i++) {
352         NapiApi::Object obj = coeffJS.Get<NapiApi::Object>(i);
353         if (!obj) {
354             // not an object in array
355             return;
356         }
357         auto x = obj.Get<float>("x");
358         auto y = obj.Get<float>("y");
359         auto z = obj.Get<float>("z");
360         if (!x || !y || !z) {
361             // invalid kind of object.
362             return;
363         }
364         coeffs.emplace_back((float)x, (float)y, (float)z);
365     }
366 
367     if (auto environment = interface_cast<SCENE_NS::IEnvironment>(GetNativeObject())) {
368         ExecSyncTask([environment, &coeffs]() {
369             environment->IrradianceCoefficients()->SetValue(coeffs);
370             return META_NS::IAny::Ptr {};
371         });
372     }
373 }
374 
GetIndirectDiffuseFactor(NapiApi::FunctionContext<> & ctx)375 napi_value EnvironmentJS::GetIndirectDiffuseFactor(NapiApi::FunctionContext<>& ctx)
376 {
377     auto node = interface_pointer_cast<SCENE_NS::IEnvironment>(GetThisNativeObject(ctx));
378     if (!node) {
379         return ctx.GetUndefined();
380     }
381     if (diffuseFactor_ == nullptr) {
382         diffuseFactor_ = BASE_NS::make_unique<Vec4Proxy>(ctx, node->IndirectDiffuseFactor());
383     }
384     return *diffuseFactor_;
385 }
386 
SetIndirectDiffuseFactor(NapiApi::FunctionContext<NapiApi::Object> & ctx)387 void EnvironmentJS::SetIndirectDiffuseFactor(NapiApi::FunctionContext<NapiApi::Object>& ctx)
388 {
389     auto node = interface_pointer_cast<SCENE_NS::IEnvironment>(GetThisNativeObject(ctx));
390     if (!node) {
391         return;
392     }
393     NapiApi::Object obj = ctx.Arg<0>();
394     if (diffuseFactor_ == nullptr) {
395         diffuseFactor_ = BASE_NS::make_unique<Vec4Proxy>(ctx, node->IndirectDiffuseFactor());
396     }
397     diffuseFactor_->SetValue(obj);
398 }
399 
GetIndirectSpecularFactor(NapiApi::FunctionContext<> & ctx)400 napi_value EnvironmentJS::GetIndirectSpecularFactor(NapiApi::FunctionContext<>& ctx)
401 {
402     auto node = interface_pointer_cast<SCENE_NS::IEnvironment>(GetThisNativeObject(ctx));
403     if (!node) {
404         return ctx.GetUndefined();
405     }
406     if (specularFactor_ == nullptr) {
407         specularFactor_ = BASE_NS::make_unique<Vec4Proxy>(ctx, node->IndirectSpecularFactor());
408     }
409     return *specularFactor_;
410 }
411 
SetIndirectSpecularFactor(NapiApi::FunctionContext<NapiApi::Object> & ctx)412 void EnvironmentJS::SetIndirectSpecularFactor(NapiApi::FunctionContext<NapiApi::Object>& ctx)
413 {
414     auto node = interface_pointer_cast<SCENE_NS::IEnvironment>(GetThisNativeObject(ctx));
415     if (!node) {
416         return;
417     }
418     NapiApi::Object obj = ctx.Arg<0>();
419     if (specularFactor_ == nullptr) {
420         specularFactor_ = BASE_NS::make_unique<Vec4Proxy>(ctx, node->IndirectSpecularFactor());
421     }
422     specularFactor_->SetValue(obj);
423 }
424 
GetEnvironmentMapFactor(NapiApi::FunctionContext<> & ctx)425 napi_value EnvironmentJS::GetEnvironmentMapFactor(NapiApi::FunctionContext<>& ctx)
426 {
427     auto node = interface_pointer_cast<SCENE_NS::IEnvironment>(GetThisNativeObject(ctx));
428     if (!node) {
429         return ctx.GetUndefined();
430     }
431     if (environmentFactor_ == nullptr) {
432         environmentFactor_ = BASE_NS::make_unique<Vec4Proxy>(ctx, node->EnvMapFactor());
433     }
434     return *environmentFactor_;
435 }
436 
SetEnvironmentMapFactor(NapiApi::FunctionContext<NapiApi::Object> & ctx)437 void EnvironmentJS::SetEnvironmentMapFactor(NapiApi::FunctionContext<NapiApi::Object>& ctx)
438 {
439     auto node = interface_pointer_cast<SCENE_NS::IEnvironment>(GetThisNativeObject(ctx));
440     if (!node) {
441         return;
442     }
443     NapiApi::Object obj = ctx.Arg<0>();
444     if (environmentFactor_ == nullptr) {
445         environmentFactor_ = BASE_NS::make_unique<Vec4Proxy>(ctx, node->EnvMapFactor());
446     }
447     environmentFactor_->SetValue(obj);
448 }
449