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