/*
* 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 "MaterialJS.h"
#include
#include
#include
#include
#include
#include
#include "SceneJS.h"
using IntfPtr = META_NS::SharedPtrIInterface;
using IntfWeakPtr = META_NS::WeakPtrIInterface;
BaseMaterial::BaseMaterial(MaterialType lt) : SceneResourceImpl(SceneResourceImpl::MATERIAL), materialType_(lt) {}
BaseMaterial::~BaseMaterial() {}
void BaseMaterial::Init(const char* class_name, napi_env env, napi_value exports, napi_callback ctor,
BASE_NS::vector& node_props)
{
SceneResourceImpl::GetPropertyDescs(node_props);
using namespace NapiApi;
node_props.push_back(TROGetProperty("materialType"));
napi_value func;
auto status = napi_define_class(
env, class_name, NAPI_AUTO_LENGTH, ctor, nullptr, node_props.size(), node_props.data(), &func);
NapiApi::MyInstanceState* mis;
napi_get_instance_data(env, (void**)&mis);
mis->StoreCtor(class_name, 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, MaterialType::x, &v); \
napi_set_named_property(env, enu, #x, v)
DECL_ENUM(eType, SHADER);
#undef DECL_ENUM
exp.Set("MaterialType", eType);
}
void* BaseMaterial::GetInstanceImpl(uint32_t id)
{
if (id == BaseMaterial::ID) {
return (BaseMaterial*)this;
}
return SceneResourceImpl::GetInstanceImpl(id);
}
void BaseMaterial::DisposeNative(TrueRootObject* tro)
{
// do nothing for now..
LOG_F("BaseMaterial::DisposeNative");
if (auto material = interface_pointer_cast(tro->GetNativeObject())) {
// reset the native object refs
tro->SetNativeObject(nullptr, false);
tro->SetNativeObject(nullptr, true);
ExecSyncTask([material = BASE_NS::move(material)]() mutable {
auto node = interface_pointer_cast(material);
if (node == nullptr) {
return META_NS::IAny::Ptr {};
}
auto scene = node->GetScene();
if (scene == nullptr) {
return META_NS::IAny::Ptr {};
}
scene->ReleaseNode(node);
return META_NS::IAny::Ptr {};
});
}
scene_.Reset();
}
napi_value BaseMaterial::GetMaterialType(NapiApi::FunctionContext<>& ctx)
{
uint32_t type = -1; // return -1 if the object does not exist anymore
if (auto node = interface_cast(GetThisNativeObject(ctx))) {
type = materialType_;
}
napi_value value;
napi_status status = napi_create_uint32(ctx, type, &value);
return value;
}
void ShaderMaterialJS::Init(napi_env env, napi_value exports)
{
BASE_NS::vector props = {
NapiApi::GetSetProperty("colorShader"),
};
BaseMaterial::Init("ShaderMaterial", env, exports, BaseObject::ctor(), props);
}
ShaderMaterialJS::ShaderMaterialJS(napi_env e, napi_callback_info i)
: BaseObject(e, i), BaseMaterial(BaseMaterial::MaterialType::SHADER)
{
// missing
NapiApi::FunctionContext fromJs(e, i);
NapiApi::Object meJs(e, fromJs.This());
NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene... (do i need it here?)
NapiApi::Object args = fromJs.Arg<1>(); // other args
scene_ = scene;
if (!GetNativeMeta(scene_.GetObject())) {
CORE_LOG_F("INVALID SCENE!");
}
auto* tro = scene.Native();
auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
sceneJS->DisposeHook((uintptr_t)&scene_, meJs);
auto metaobj = GetNativeObjectParam(args); // Should be IMaterial
SetNativeObject(metaobj, true);
StoreJsObj(metaobj, meJs);
BASE_NS::string name;
if (auto prm = args.Get("name")) {
name = prm;
} else {
if (auto named = interface_cast(metaobj)) {
name = named->Name()->GetValue();
}
}
meJs.Set("name", name);
}
ShaderMaterialJS::~ShaderMaterialJS() {}
void* ShaderMaterialJS::GetInstanceImpl(uint32_t id)
{
if (id == ShaderMaterialJS::ID) {
return this;
}
return BaseMaterial::GetInstanceImpl(id);
}
void ShaderMaterialJS::DisposeNative()
{
NapiApi::Object obj = scene_.GetObject();
if (obj) {
auto* tro = obj.Native();
SceneJS* sceneJS;
if (tro) {
sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
sceneJS->ReleaseDispose((uintptr_t)&scene_);
}
}
if (auto material = interface_pointer_cast(GetNativeObject())) {
SetNativeObject(nullptr, false);
SetNativeObject(nullptr, true);
if (obj) {
ExecSyncTask([mat = BASE_NS::move(material)]() -> META_NS::IAny::Ptr {
mat->MaterialShader()->SetValue(nullptr);
return {};
});
}
} else {
SetNativeObject(nullptr, false);
}
shader_.Reset();
BaseMaterial::DisposeNative(this);
}
void ShaderMaterialJS::Finalize(napi_env env)
{
BaseObject::Finalize(env);
}
void ShaderMaterialJS::SetColorShader(NapiApi::FunctionContext& ctx)
{
NapiApi::Object shaderJS = ctx.Arg<0>();
auto material = interface_pointer_cast(GetNativeObject());
if (!material) {
shader_.Reset();
return;
}
// handle the case where a "bound shader" is attached too.
auto shader = GetNativeMeta(shaderJS);
if (shader == nullptr) {
// attaching to a bound shader. (if shader was bound to another material, we need to make a new copy)
auto boundShader = GetNativeMeta(shaderJS);
return;
}
// bind it to material (in native)
ExecSyncTask([material, &shader]() -> META_NS::IAny::Ptr {
material->MaterialShader()->SetValue(shader);
material->MaterialShaderState()->SetValue(nullptr);
return {};
});
// construct a "bound" shader object from the "non bound" one.
NapiApi::Object parms(ctx);
napi_value args[] = {
scene_.GetValue(), // <- get the scene
parms // other constructor parameters
};
parms.Set("name", shaderJS.Get("name"));
parms.Set("Material", ctx.This()); // js material object that we are bound to.
shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
interface_cast(shaderBind_)
->AddProperty(META_NS::ConstructProperty(
"shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
interface_cast(shaderBind_)
->GetPropertyByName("shader")
->SetValue(interface_pointer_cast(shader));
auto argc = BASE_NS::countof(args);
auto argv = args;
MakeNativeObjectParam(ctx, shaderBind_, argc, argv);
auto result = CreateJsObj(ctx, "Shader", shaderBind_, false, argc, argv);
shader_ = StoreJsObj(shaderBind_, NapiApi::Object(ctx, result));
}
napi_value ShaderMaterialJS::GetColorShader(NapiApi::FunctionContext<>& ctx)
{
auto material = interface_pointer_cast(GetNativeObject());
if (!material) {
shader_.Reset();
return ctx.GetNull();
}
if (shader_.IsEmpty()) {
// no shader set yet..
// see if we have one on the native side.
// and create the "bound shader" object from it.
// check native side..
SCENE_NS::IShader::Ptr shader;
ExecSyncTask([material, &shader]() -> META_NS::IAny::Ptr {
shader = material->MaterialShader()->GetValue();
return {};
});
if (!shader) {
// no shader in native also.
return ctx.GetNull();
}
// construct a "bound" shader object from the "non bound" one.
NapiApi::Object parms(ctx);
napi_value args[] = {
scene_.GetValue(), // <- get the scene
parms // other constructor parameters
};
if (!GetNativeMeta(scene_.GetObject())) {
CORE_LOG_F("INVALID SCENE!");
}
parms.Set("Material", ctx.This()); // js material object that we are bound to.
shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
interface_cast(shaderBind_)
->AddProperty(META_NS::ConstructProperty(
"shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
interface_cast(shaderBind_)
->GetPropertyByName("shader")
->SetValue(interface_pointer_cast(shader));
auto argc = BASE_NS::countof(args);
auto argv = args;
MakeNativeObjectParam(ctx, shaderBind_, argc, argv);
auto result = CreateJsObj(ctx, "Shader", shaderBind_, false, argc, argv);
shader_ = StoreJsObj(shaderBind_, NapiApi::Object(ctx, result));
}
return shader_.GetValue();
}