/*
* Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EnvironmentJS.h"
#include
#include
#include
#include
#include // for the classid..
#include
#include
#include
#include
#include
#include "SceneJS.h"
using namespace SCENE_NS;
void EnvironmentJS::Init(napi_env env, napi_value exports)
{
using namespace NapiApi;
BASE_NS::vector node_props;
SceneResourceImpl::GetPropertyDescs(node_props);
// clang-format off
node_props.emplace_back(GetSetProperty("backgroundType"));
node_props.emplace_back(GetSetProperty("environmentImage"));
node_props.emplace_back(GetSetProperty("radianceImage"));
node_props.emplace_back(GetSetProperty("irradianceCoefficients"));
node_props.emplace_back(GetSetProperty("indirectDiffuseFactor"));
node_props.emplace_back(GetSetProperty("indirectSpecularFactor"));
node_props.emplace_back(GetSetProperty("environmentMapFactor"));
// clang-format on
napi_value func;
auto status = napi_define_class(env, "Environment", NAPI_AUTO_LENGTH, BaseObject::ctor(), nullptr,
node_props.size(), node_props.data(), &func);
NapiApi::MyInstanceState* mis;
napi_get_instance_data(env, (void**)&mis);
mis->StoreCtor("Environment", func);
NapiApi::Object exp(env, exports);
napi_value eType;
napi_value v;
napi_create_object(env, &eType);
#define DECL_ENUM(enu, x) \
napi_create_uint32(env, EnvironmentBackgroundType::x, &v); \
napi_set_named_property(env, enu, #x, v)
DECL_ENUM(eType, BACKGROUND_NONE);
DECL_ENUM(eType, BACKGROUND_IMAGE);
DECL_ENUM(eType, BACKGROUND_CUBEMAP);
DECL_ENUM(eType, BACKGROUND_EQUIRECTANGULAR);
#undef DECL_ENUM
exp.Set("EnvironmentBackgroundType", eType);
}
napi_value EnvironmentJS::Dispose(NapiApi::FunctionContext<>& ctx)
{
LOG_F("EnvironmentJS::Dispose");
DisposeNative();
return {};
}
void EnvironmentJS::DisposeNative()
{
if (!disposed_) {
CORE_LOG_F("EnvironmentJS::DisposeNative");
disposed_ = true;
NapiApi::Object obj = scene_.GetObject();
auto* tro = obj.Native();
if (tro) {
SceneJS* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
if (sceneJS) {
sceneJS->ReleaseStrongDispose((uintptr_t)&scene_);
}
}
diffuseFactor_.reset();
specularFactor_.reset();
environmentFactor_.reset();
if (auto env = interface_pointer_cast(GetNativeObject())) {
// reset the native object refs
SetNativeObject(nullptr, false);
SetNativeObject(nullptr, true);
diffuseFactor_.reset();
specularFactor_.reset();
environmentFactor_.reset();
NapiApi::Object sceneJS = scene_.GetObject();
if (sceneJS) {
napi_value null;
napi_get_null(sceneJS.GetEnv(), &null);
sceneJS.Set("environment", null);
scene_.Reset();
auto* tro = sceneJS.Native();
IScene::Ptr scene = interface_pointer_cast(tro->GetNativeObject());
ExecSyncTask([s = BASE_NS::move(scene), e = BASE_NS::move(env)]() {
auto en = interface_pointer_cast(e);
s->ReleaseNode(en);
en.reset();
return META_NS::IAny::Ptr {};
});
}
}
}
scene_.Reset();
}
void* EnvironmentJS::GetInstanceImpl(uint32_t id)
{
if (id == EnvironmentJS::ID) {
return this;
}
return SceneResourceImpl::GetInstanceImpl(id);
}
void EnvironmentJS::Finalize(napi_env env)
{
// hmm.. do i need to do something BEFORE the object gets deleted..
DisposeNative();
BaseObject::Finalize(env);
}
EnvironmentJS::EnvironmentJS(napi_env e, napi_callback_info i)
: BaseObject(e, i), SceneResourceImpl(SceneResourceImpl::ENVIRONMENT)
{
LOG_F("EnvironmentJS ++");
NapiApi::FunctionContext fromJs(e, i);
if (!fromJs) {
// no arguments. so internal create.
// expecting caller to finish
return;
}
scene_ = { fromJs, fromJs.Arg<0>() };
if (!GetNativeMeta(scene_.GetObject())) {
CORE_LOG_F("INVALID SCENE!");
}
NapiApi::Object meJs(e, fromJs.This());
auto* tro = scene_.GetObject().Native();
auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
sceneJS->StrongDisposeHook((uintptr_t)&scene_, meJs);
IScene::Ptr scene = interface_pointer_cast(tro->GetNativeObject());
NapiApi::Value name;
NapiApi::Object args = fromJs.Arg<1>();
if (auto prm = args.Get("name")) {
name = NapiApi::Value(e, prm);
}
BASE_NS::string nameS = name;
if (nameS.empty()) {
// create "unique" name
nameS = BASE_NS::to_string((uint64_t)this);
}
IEnvironment::Ptr env = GetNativeMeta(meJs);
// Construct native object (if needed)
if (!env) {
ExecSyncTask([&env, scene, nameS]() {
BASE_NS::string_view n = nameS; /*nodepath actually*/
env = scene->CreateNode(nameS);
return META_NS::IAny::Ptr {};
});
}
// process constructor args
// weak ref, due to being owned by the scene.
SetNativeObject(interface_pointer_cast(env), false);
StoreJsObj(interface_pointer_cast(env), meJs);
env.reset();
if (name) {
// set the name of the object. if we were given one
meJs.Set("name", name);
}
}
EnvironmentJS::~EnvironmentJS()
{
LOG_F("EnvironmentJS --");
DisposeNative();
if (!GetNativeObject()) {
return;
}
}
napi_value EnvironmentJS::GetBackgroundType(NapiApi::FunctionContext<>& ctx)
{
uint32_t typeI = 0;
if (auto env = interface_cast(GetNativeObject())) {
ExecSyncTask([env, &typeI]() {
typeI = env->Background()->GetValue();
return META_NS::IAny::Ptr {};
});
}
return NapiApi::Value(ctx, (uint32_t)typeI);
}
void EnvironmentJS::SetBackgroundType(NapiApi::FunctionContext& ctx)
{
if (auto env = interface_cast(GetNativeObject())) {
uint32_t typeI = ctx.Arg<0>();
auto typeE = static_cast(typeI);
IEnvironment::BackgroundType type;
switch (typeE) {
case EnvironmentBackgroundType::BACKGROUND_NONE:
type = IEnvironment::BackgroundType::NONE;
break;
case EnvironmentBackgroundType::BACKGROUND_IMAGE:
type = IEnvironment::BackgroundType::IMAGE;
break;
case EnvironmentBackgroundType::BACKGROUND_CUBEMAP:
type = IEnvironment::BackgroundType::CUBEMAP;
break;
case EnvironmentBackgroundType::BACKGROUND_EQUIRECTANGULAR:
type = IEnvironment::BackgroundType::EQUIRECTANGULAR;
break;
default:
type = IEnvironment::BackgroundType::NONE;
break;
}
ExecSyncTask([env, &type]() {
env->Background()->SetValue(type);
return META_NS::IAny::Ptr {};
});
}
}
napi_value EnvironmentJS::GetEnvironmentImage(NapiApi::FunctionContext<>& ctx)
{
if (auto environment = interface_cast(GetNativeObject())) {
SCENE_NS::IBitmap::Ptr image;
ExecSyncTask([environment, &image]() {
image = environment->EnvironmentImage()->GetValue();
return META_NS::IAny::Ptr {};
});
auto obj = interface_pointer_cast(image);
if (auto cached = FetchJsObj(obj)) {
return cached;
}
napi_value args[] = { scene_.GetValue(), NapiApi::Object(ctx) };
return CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args);
}
return ctx.GetNull();
}
void EnvironmentJS::SetEnvironmentImage(NapiApi::FunctionContext& ctx)
{
NapiApi::Object imageJS = ctx.Arg<0>();
SCENE_NS::IBitmap::Ptr image;
if (auto nat = imageJS.Native()) {
image = interface_pointer_cast(nat->GetNativeObject());
}
if (auto environment = interface_cast(GetNativeObject())) {
ExecSyncTask([environment, image]() {
environment->EnvironmentImage()->SetValue(image);
return META_NS::IAny::Ptr {};
});
}
}
napi_value EnvironmentJS::GetRadianceImage(NapiApi::FunctionContext<>& ctx)
{
if (auto environment = interface_cast(GetNativeObject())) {
SCENE_NS::IBitmap::Ptr image;
ExecSyncTask([environment, &image]() {
image = environment->RadianceImage()->GetValue();
return META_NS::IAny::Ptr {};
});
auto obj = interface_pointer_cast(image);
if (auto cached = FetchJsObj(obj)) {
return cached;
}
napi_value args[] = { scene_.GetValue(), NapiApi::Object(ctx) };
return CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args);
}
return ctx.GetNull();
}
void EnvironmentJS::SetRadianceImage(NapiApi::FunctionContext& ctx)
{
NapiApi::Object imageJS = ctx.Arg<0>();
SCENE_NS::IBitmap::Ptr image;
if (auto nat = imageJS.Native()) {
image = interface_pointer_cast(nat->GetNativeObject());
}
if (auto environment = interface_cast(GetNativeObject())) {
ExecSyncTask([environment, image]() {
environment->RadianceImage()->SetValue(image);
return META_NS::IAny::Ptr {};
});
}
}
napi_value EnvironmentJS::GetIrradianceCoefficients(NapiApi::FunctionContext<>& ctx)
{
BASE_NS::vector coeffs;
if (auto environment = interface_cast(GetNativeObject())) {
ExecSyncTask([environment, &coeffs]() {
coeffs = environment->IrradianceCoefficients()->GetValue();
return META_NS::IAny::Ptr {};
});
}
NapiApi::Array res(ctx, 9); // array size 9
size_t index = 0;
for (auto& v : coeffs) {
NapiApi::Object vec(ctx);
vec.Set("x", NapiApi::Value(ctx, v.x));
vec.Set("y", NapiApi::Value(ctx, v.y));
vec.Set("z", NapiApi::Value(ctx, v.z));
res.Set(index++, vec);
}
return res;
}
void EnvironmentJS::SetIrradianceCoefficients(NapiApi::FunctionContext& ctx)
{
NapiApi::Array coeffJS = ctx.Arg<0>();
if (coeffJS.Count() != 9) { // array size 9
// not enough elements in array
return;
}
BASE_NS::vector coeffs;
for (auto i = 0; i < coeffJS.Count(); i++) {
NapiApi::Object obj = coeffJS.Get(i);
if (!obj) {
// not an object in array
return;
}
auto x = obj.Get("x");
auto y = obj.Get("y");
auto z = obj.Get("z");
if (!x || !y || !z) {
// invalid kind of object.
return;
}
coeffs.emplace_back((float)x, (float)y, (float)z);
}
if (auto environment = interface_cast(GetNativeObject())) {
ExecSyncTask([environment, &coeffs]() {
environment->IrradianceCoefficients()->SetValue(coeffs);
return META_NS::IAny::Ptr {};
});
}
}
napi_value EnvironmentJS::GetIndirectDiffuseFactor(NapiApi::FunctionContext<>& ctx)
{
auto node = interface_pointer_cast(GetThisNativeObject(ctx));
if (!node) {
return ctx.GetUndefined();
}
if (diffuseFactor_ == nullptr) {
diffuseFactor_ = BASE_NS::make_unique(ctx, node->IndirectDiffuseFactor());
}
return *diffuseFactor_;
}
void EnvironmentJS::SetIndirectDiffuseFactor(NapiApi::FunctionContext& ctx)
{
auto node = interface_pointer_cast(GetThisNativeObject(ctx));
if (!node) {
return;
}
NapiApi::Object obj = ctx.Arg<0>();
if (diffuseFactor_ == nullptr) {
diffuseFactor_ = BASE_NS::make_unique(ctx, node->IndirectDiffuseFactor());
}
diffuseFactor_->SetValue(obj);
}
napi_value EnvironmentJS::GetIndirectSpecularFactor(NapiApi::FunctionContext<>& ctx)
{
auto node = interface_pointer_cast(GetThisNativeObject(ctx));
if (!node) {
return ctx.GetUndefined();
}
if (specularFactor_ == nullptr) {
specularFactor_ = BASE_NS::make_unique(ctx, node->IndirectSpecularFactor());
}
return *specularFactor_;
}
void EnvironmentJS::SetIndirectSpecularFactor(NapiApi::FunctionContext& ctx)
{
auto node = interface_pointer_cast(GetThisNativeObject(ctx));
if (!node) {
return;
}
NapiApi::Object obj = ctx.Arg<0>();
if (specularFactor_ == nullptr) {
specularFactor_ = BASE_NS::make_unique(ctx, node->IndirectSpecularFactor());
}
specularFactor_->SetValue(obj);
}
napi_value EnvironmentJS::GetEnvironmentMapFactor(NapiApi::FunctionContext<>& ctx)
{
auto node = interface_pointer_cast(GetThisNativeObject(ctx));
if (!node) {
return ctx.GetUndefined();
}
if (environmentFactor_ == nullptr) {
environmentFactor_ = BASE_NS::make_unique(ctx, node->EnvMapFactor());
}
return *environmentFactor_;
}
void EnvironmentJS::SetEnvironmentMapFactor(NapiApi::FunctionContext& ctx)
{
auto node = interface_pointer_cast(GetThisNativeObject(ctx));
if (!node) {
return;
}
NapiApi::Object obj = ctx.Arg<0>();
if (environmentFactor_ == nullptr) {
environmentFactor_ = BASE_NS::make_unique(ctx, node->EnvMapFactor());
}
environmentFactor_->SetValue(obj);
}