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 "MaterialJS.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 <scene_plugin/api/light_uid.h>
21 #include <scene_plugin/interface/intf_light.h>
22 #include <scene_plugin/interface/intf_scene.h>
23
24 #include "SceneJS.h"
25 using IntfPtr = META_NS::SharedPtrIInterface;
26 using IntfWeakPtr = META_NS::WeakPtrIInterface;
27
BaseMaterial(MaterialType lt)28 BaseMaterial::BaseMaterial(MaterialType lt) : SceneResourceImpl(SceneResourceImpl::MATERIAL), materialType_(lt) {}
~BaseMaterial()29 BaseMaterial::~BaseMaterial() {}
Init(const char * class_name,napi_env env,napi_value exports,napi_callback ctor,BASE_NS::vector<napi_property_descriptor> & node_props)30 void BaseMaterial::Init(const char* class_name, napi_env env, napi_value exports, napi_callback ctor,
31 BASE_NS::vector<napi_property_descriptor>& node_props)
32 {
33 SceneResourceImpl::GetPropertyDescs(node_props);
34
35 using namespace NapiApi;
36 node_props.push_back(TROGetProperty<float, BaseMaterial, &BaseMaterial::GetMaterialType>("materialType"));
37
38 napi_value func;
39 auto status = napi_define_class(
40 env, class_name, NAPI_AUTO_LENGTH, ctor, nullptr, node_props.size(), node_props.data(), &func);
41
42 NapiApi::MyInstanceState* mis;
43 napi_get_instance_data(env, (void**)&mis);
44 mis->StoreCtor(class_name, func);
45
46 NapiApi::Object exp(env, exports);
47
48 napi_value eType;
49 napi_value v;
50 napi_create_object(env, &eType);
51 #define DECL_ENUM(enu, x) \
52 napi_create_uint32(env, MaterialType::x, &v); \
53 napi_set_named_property(env, enu, #x, v)
54
55 DECL_ENUM(eType, SHADER);
56 #undef DECL_ENUM
57 exp.Set("MaterialType", eType);
58 }
59
GetInstanceImpl(uint32_t id)60 void* BaseMaterial::GetInstanceImpl(uint32_t id)
61 {
62 if (id == BaseMaterial::ID) {
63 return (BaseMaterial*)this;
64 }
65 return SceneResourceImpl::GetInstanceImpl(id);
66 }
DisposeNative(TrueRootObject * tro)67 void BaseMaterial::DisposeNative(TrueRootObject* tro)
68 {
69 // do nothing for now..
70 LOG_F("BaseMaterial::DisposeNative");
71 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(tro->GetNativeObject())) {
72 // reset the native object refs
73 tro->SetNativeObject(nullptr, false);
74 tro->SetNativeObject(nullptr, true);
75
76 ExecSyncTask([material = BASE_NS::move(material)]() mutable {
77 auto node = interface_pointer_cast<SCENE_NS::INode>(material);
78 if (node == nullptr) {
79 return META_NS::IAny::Ptr {};
80 }
81 auto scene = node->GetScene();
82 if (scene == nullptr) {
83 return META_NS::IAny::Ptr {};
84 }
85 scene->ReleaseNode(node);
86 return META_NS::IAny::Ptr {};
87 });
88 }
89 scene_.Reset();
90 }
GetMaterialType(NapiApi::FunctionContext<> & ctx)91 napi_value BaseMaterial::GetMaterialType(NapiApi::FunctionContext<>& ctx)
92 {
93 uint32_t type = -1; // return -1 if the object does not exist anymore
94 if (auto node = interface_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
95 type = materialType_;
96 }
97 napi_value value;
98 napi_status status = napi_create_uint32(ctx, type, &value);
99 return value;
100 }
101
Init(napi_env env,napi_value exports)102 void ShaderMaterialJS::Init(napi_env env, napi_value exports)
103 {
104 BASE_NS::vector<napi_property_descriptor> props = {
105 NapiApi::GetSetProperty<NapiApi::Object, ShaderMaterialJS, &ShaderMaterialJS::GetColorShader,
106 &ShaderMaterialJS::SetColorShader>("colorShader"),
107 };
108
109 BaseMaterial::Init("ShaderMaterial", env, exports, BaseObject::ctor<ShaderMaterialJS>(), props);
110 }
111
ShaderMaterialJS(napi_env e,napi_callback_info i)112 ShaderMaterialJS::ShaderMaterialJS(napi_env e, napi_callback_info i)
113 : BaseObject<ShaderMaterialJS>(e, i), BaseMaterial(BaseMaterial::MaterialType::SHADER)
114 {
115 // missing
116 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
117 NapiApi::Object meJs(e, fromJs.This());
118
119 NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene... (do i need it here?)
120 NapiApi::Object args = fromJs.Arg<1>(); // other args
121
122 scene_ = scene;
123 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
124 CORE_LOG_F("INVALID SCENE!");
125 }
126
127 auto* tro = scene.Native<TrueRootObject>();
128 auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
129 sceneJS->DisposeHook((uintptr_t)&scene_, meJs);
130
131 auto metaobj = GetNativeObjectParam<META_NS::IObject>(args); // Should be IMaterial
132 SetNativeObject(metaobj, true);
133 StoreJsObj(metaobj, meJs);
134
135 BASE_NS::string name;
136 if (auto prm = args.Get<BASE_NS::string>("name")) {
137 name = prm;
138 } else {
139 if (auto named = interface_cast<META_NS::INamed>(metaobj)) {
140 name = named->Name()->GetValue();
141 }
142 }
143 meJs.Set("name", name);
144 }
145
~ShaderMaterialJS()146 ShaderMaterialJS::~ShaderMaterialJS() {}
GetInstanceImpl(uint32_t id)147 void* ShaderMaterialJS::GetInstanceImpl(uint32_t id)
148 {
149 if (id == ShaderMaterialJS::ID) {
150 return this;
151 }
152 return BaseMaterial::GetInstanceImpl(id);
153 }
DisposeNative()154 void ShaderMaterialJS::DisposeNative()
155 {
156 NapiApi::Object obj = scene_.GetObject();
157 if (obj) {
158 auto* tro = obj.Native<TrueRootObject>();
159 SceneJS* sceneJS;
160 if (tro) {
161 sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
162 sceneJS->ReleaseDispose((uintptr_t)&scene_);
163 }
164 }
165 if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject())) {
166 SetNativeObject(nullptr, false);
167 SetNativeObject(nullptr, true);
168 if (obj) {
169 ExecSyncTask([mat = BASE_NS::move(material)]() -> META_NS::IAny::Ptr {
170 mat->MaterialShader()->SetValue(nullptr);
171 return {};
172 });
173 }
174 } else {
175 SetNativeObject(nullptr, false);
176 }
177 shader_.Reset();
178
179 BaseMaterial::DisposeNative(this);
180 }
Finalize(napi_env env)181 void ShaderMaterialJS::Finalize(napi_env env)
182 {
183 BaseObject::Finalize(env);
184 }
185
SetColorShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)186 void ShaderMaterialJS::SetColorShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
187 {
188 NapiApi::Object shaderJS = ctx.Arg<0>();
189 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
190 if (!material) {
191 shader_.Reset();
192 return;
193 }
194 // handle the case where a "bound shader" is attached too.
195 auto shader = GetNativeMeta<SCENE_NS::IShader>(shaderJS);
196 if (shader == nullptr) {
197 // attaching to a bound shader. (if shader was bound to another material, we need to make a new copy)
198 auto boundShader = GetNativeMeta<META_NS::IObject>(shaderJS);
199 return;
200 }
201 // bind it to material (in native)
202 ExecSyncTask([material, &shader]() -> META_NS::IAny::Ptr {
203 material->MaterialShader()->SetValue(shader);
204 material->MaterialShaderState()->SetValue(nullptr);
205 return {};
206 });
207
208 // construct a "bound" shader object from the "non bound" one.
209 NapiApi::Object parms(ctx);
210 napi_value args[] = {
211 scene_.GetValue(), // <- get the scene
212 parms // other constructor parameters
213 };
214
215 parms.Set("name", shaderJS.Get("name"));
216 parms.Set("Material", ctx.This()); // js material object that we are bound to.
217
218 shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
219 interface_cast<META_NS::IMetadata>(shaderBind_)
220 ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
221 "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
222 interface_cast<META_NS::IMetadata>(shaderBind_)
223 ->GetPropertyByName<IntfPtr>("shader")
224 ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
225
226 auto argc = BASE_NS::countof(args);
227 auto argv = args;
228 MakeNativeObjectParam(ctx, shaderBind_, argc, argv);
229 auto result = CreateJsObj(ctx, "Shader", shaderBind_, false, argc, argv);
230 shader_ = StoreJsObj(shaderBind_, NapiApi::Object(ctx, result));
231 }
GetColorShader(NapiApi::FunctionContext<> & ctx)232 napi_value ShaderMaterialJS::GetColorShader(NapiApi::FunctionContext<>& ctx)
233 {
234 auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
235 if (!material) {
236 shader_.Reset();
237 return ctx.GetNull();
238 }
239 if (shader_.IsEmpty()) {
240 // no shader set yet..
241 // see if we have one on the native side.
242 // and create the "bound shader" object from it.
243
244 // check native side..
245 SCENE_NS::IShader::Ptr shader;
246 ExecSyncTask([material, &shader]() -> META_NS::IAny::Ptr {
247 shader = material->MaterialShader()->GetValue();
248 return {};
249 });
250 if (!shader) {
251 // no shader in native also.
252 return ctx.GetNull();
253 }
254
255 // construct a "bound" shader object from the "non bound" one.
256 NapiApi::Object parms(ctx);
257 napi_value args[] = {
258 scene_.GetValue(), // <- get the scene
259 parms // other constructor parameters
260 };
261
262 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
263 CORE_LOG_F("INVALID SCENE!");
264 }
265 parms.Set("Material", ctx.This()); // js material object that we are bound to.
266
267 shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
268 interface_cast<META_NS::IMetadata>(shaderBind_)
269 ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
270 "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
271 interface_cast<META_NS::IMetadata>(shaderBind_)
272 ->GetPropertyByName<IntfPtr>("shader")
273 ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
274
275 auto argc = BASE_NS::countof(args);
276 auto argv = args;
277 MakeNativeObjectParam(ctx, shaderBind_, argc, argv);
278 auto result = CreateJsObj(ctx, "Shader", shaderBind_, false, argc, argv);
279 shader_ = StoreJsObj(shaderBind_, NapiApi::Object(ctx, result));
280 }
281 return shader_.GetValue();
282 }
283