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" 30using namespace SCENE_NS; 31 32void 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 82napi_value EnvironmentJS::Dispose(NapiApi::FunctionContext<>& ctx) 83{ 84 LOG_F("EnvironmentJS::Dispose"); 85 DisposeNative(); 86 return {}; 87} 88void 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} 133void* EnvironmentJS::GetInstanceImpl(uint32_t id) 134{ 135 if (id == EnvironmentJS::ID) { 136 return this; 137 } 138 return SceneResourceImpl::GetInstanceImpl(id); 139} 140void 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 147EnvironmentJS::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 204EnvironmentJS::~EnvironmentJS() 205{ 206 LOG_F("EnvironmentJS --"); 207 DisposeNative(); 208 if (!GetNativeObject()) { 209 return; 210 } 211} 212 213napi_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 225void 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} 254napi_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 274void 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 289napi_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 309void 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} 323napi_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} 343void 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 375napi_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 387void 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 400napi_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 412void 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 425napi_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 437void 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