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