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