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 "ShaderJS.h"
16
17 #include <scene_plugin/api/material_uid.h>
18 #include <scene_plugin/interface/intf_ecs_object.h>
19 #include <scene_plugin/interface/intf_mesh.h>
20 #include <scene_plugin/interface/intf_scene.h>
21
22 #include "MaterialJS.h"
23 #include "SceneJS.h"
24 #include "Vec2Proxy.h"
25 #include "Vec3Proxy.h"
26 #include "Vec4Proxy.h"
27
28 using IntfPtr = META_NS::SharedPtrIInterface;
29 using IntfWeakPtr = META_NS::WeakPtrIInterface;
30
31 template<typename type>
32 class TypeProxy : public Proxy {
33 META_NS::IProperty::Ptr prop_;
34 BASE_NS::string name_;
35
36 public:
TypeProxy(META_NS::IProperty::Ptr prop)37 explicit TypeProxy(META_NS::IProperty::Ptr prop) : prop_(prop), name_(prop_->GetName()) {}
~TypeProxy()38 ~TypeProxy()
39 {
40 prop_.reset();
41 }
42
Get(NapiApi::FunctionContext<> ctx)43 napi_value Get(NapiApi::FunctionContext<> ctx)
44 {
45 META_NS::Property<type> p(prop_);
46 return NapiApi::Value<type>(ctx, p->GetValue());
47 }
Set(NapiApi::FunctionContext<type> ctx)48 napi_value Set(NapiApi::FunctionContext<type> ctx)
49 {
50 type val = ctx.template Arg<0>();
51 META_NS::Property<type> p(prop_);
52 p->SetValue(val);
53 return ctx.GetUndefined();
54 }
insertProp(BASE_NS::vector<napi_property_descriptor> & props)55 void insertProp(BASE_NS::vector<napi_property_descriptor>& props)
56 {
57 props.push_back(napi_property_descriptor { name_.c_str(), nullptr, nullptr,
58 [](napi_env e, napi_callback_info i) -> napi_value {
59 NapiApi::FunctionContext<> info(e, i);
60 auto me_ = (TypeProxy<type>*)info.GetData();
61 return me_->Get(info);
62 },
63 [](napi_env e, napi_callback_info i) -> napi_value {
64 NapiApi::FunctionContext<type> info(e, i);
65 auto me_ = (TypeProxy<type>*)info.GetData();
66 return me_->Set(info);
67 },
68 nullptr, napi_default_jsproperty, (void*)this });
69 }
70 };
71
72 class BitmapProxy : public Proxy {
73 NapiApi::StrongRef scene_;
74 META_NS::IProperty::Ptr prop_;
75 BASE_NS::string name_;
76
77 public:
BitmapProxy(NapiApi::Object scene,META_NS::IProperty::Ptr prop,BASE_NS::string_view prefix="")78 BitmapProxy(NapiApi::Object scene, META_NS::IProperty::Ptr prop, BASE_NS::string_view prefix = "") : prop_(prop)
79 {
80 scene_ = scene;
81 if (!prefix.empty()) {
82 name_ = prefix;
83 name_ += "_";
84 }
85 name_ += prop->GetName();
86 }
~BitmapProxy()87 ~BitmapProxy()
88 {
89 prop_.reset();
90 }
91
Get(NapiApi::FunctionContext<> ctx)92 napi_value Get(NapiApi::FunctionContext<> ctx)
93 {
94 META_NS::Property<SCENE_NS::IBitmap::Ptr> p(prop_);
95 // return NapiApi::Value<type>(ctx, p->GetValue());
96 auto obj = p->GetValue();
97 if (auto cached = FetchJsObj(obj)) {
98 return cached;
99 }
100
101 if (obj) {
102 // okay.. there is a native bitmap set.. but no js wrapper yet.
103
104 // create the jsobject if we don't have one.
105 NapiApi::Object parms(ctx);
106 napi_value args[] = {
107 scene_.GetValue(), // scene..
108 parms // params.
109 };
110 MakeNativeObjectParam(ctx, obj, BASE_NS::countof(args), args);
111
112 auto size = obj->Size()->GetValue();
113 auto uri = obj->Uri()->GetValue();
114 auto name = interface_cast<META_NS::INamed>(obj)->Name()->GetValue();
115 parms.Set("uri", uri);
116 NapiApi::Object imageJS(GetJSConstructor(ctx, "Image"), BASE_NS::countof(args), args);
117 return imageJS;
118 }
119 return ctx.GetNull();
120 }
Set(NapiApi::FunctionContext<NapiApi::Object> ctx)121 napi_value Set(NapiApi::FunctionContext<NapiApi::Object> ctx)
122 {
123 NapiApi::Object val = ctx.Arg<0>();
124 auto bitmap = GetNativeMeta<SCENE_NS::IBitmap>(val);
125 if (bitmap) {
126 META_NS::Property<SCENE_NS::IBitmap::Ptr> p(prop_);
127 p->SetValue(bitmap);
128 }
129 return ctx.GetUndefined();
130 }
insertProp(BASE_NS::vector<napi_property_descriptor> & props)131 void insertProp(BASE_NS::vector<napi_property_descriptor>& props)
132 {
133 props.push_back(napi_property_descriptor { name_.c_str(), nullptr, nullptr,
134 [](napi_env e, napi_callback_info i) -> napi_value {
135 NapiApi::FunctionContext<> info(e, i);
136 auto me_ = (BitmapProxy*)info.GetData();
137 return me_->Get(info);
138 },
139 [](napi_env e, napi_callback_info i) -> napi_value {
140 NapiApi::FunctionContext<NapiApi::Object> info(e, i);
141 auto me_ = (BitmapProxy*)info.GetData();
142 return me_->Set(info);
143 },
144 nullptr, napi_default_jsproperty, (void*)this });
145 }
146 };
147
148 template<typename proxType>
149 class PropProxy : public Proxy {
150 BASE_NS::shared_ptr<proxType> proxy_;
151 BASE_NS::string name_;
152
153 public:
PropProxy(napi_env e,META_NS::IProperty::Ptr prop,BASE_NS::string_view prefix="")154 PropProxy(napi_env e, META_NS::IProperty::Ptr prop, BASE_NS::string_view prefix = "")
155 {
156 proxy_.reset(new proxType(e, prop));
157 if (!prefix.empty()) {
158 name_ = prefix;
159 name_ += "_";
160 }
161 name_ += prop->GetName();
162 }
~PropProxy()163 ~PropProxy()
164 {
165 proxy_.reset();
166 }
Get(NapiApi::FunctionContext<> ctx)167 napi_value Get(NapiApi::FunctionContext<> ctx)
168 {
169 return proxy_->Value();
170 }
171
Set(NapiApi::FunctionContext<NapiApi::Object> ctx)172 napi_value Set(NapiApi::FunctionContext<NapiApi::Object> ctx)
173 {
174 NapiApi::Object val = ctx.Arg<0>();
175 proxy_->SetValue(val);
176 return ctx.GetUndefined();
177 }
178
insertProp(BASE_NS::vector<napi_property_descriptor> & props)179 void insertProp(BASE_NS::vector<napi_property_descriptor>& props)
180 {
181 props.push_back(napi_property_descriptor { name_.c_str(), nullptr, nullptr,
182 [](napi_env e, napi_callback_info i) -> napi_value {
183 NapiApi::FunctionContext<> info(e, i);
184 auto me_ = (PropProxy<proxType>*)info.GetData();
185 return me_->Get(info);
186 },
187 [](napi_env e, napi_callback_info i) -> napi_value {
188 NapiApi::FunctionContext<NapiApi::Object> info(e, i);
189 auto me_ = (PropProxy<proxType>*)info.GetData();
190 return me_->Set(info);
191 },
192 nullptr, napi_default_jsproperty, (void*)this });
193 }
194 };
195
Init(napi_env env,napi_value exports)196 void ShaderJS::Init(napi_env env, napi_value exports)
197 {
198 using namespace NapiApi;
199 BASE_NS::vector<napi_property_descriptor> node_props;
200
201 SceneResourceImpl::GetPropertyDescs(node_props);
202
203 napi_value func;
204 auto status = napi_define_class(env, "Shader", NAPI_AUTO_LENGTH, BaseObject::ctor<ShaderJS>(), nullptr,
205 node_props.size(), node_props.data(), &func);
206
207 NapiApi::MyInstanceState* mis;
208 napi_get_instance_data(env, (void**)&mis);
209 mis->StoreCtor("Shader", func);
210 }
211
ShaderJS(napi_env e,napi_callback_info i)212 ShaderJS::ShaderJS(napi_env e, napi_callback_info i)
213 : BaseObject<ShaderJS>(e, i), SceneResourceImpl(SceneResourceImpl::SHADER)
214 {
215 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
216 NapiApi::Object meJs(e, fromJs.This());
217
218 NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene...
219 NapiApi::Object args = fromJs.Arg<1>(); // other args
220 scene_ = { scene };
221 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
222 CORE_LOG_F("INVALID SCENE!");
223 }
224
225 auto* tro = scene.Native<TrueRootObject>();
226 auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
227 sceneJS->DisposeHook((uintptr_t)&scene_, meJs);
228
229 // check if we got the NativeObject as parameter. (meta object created when bound to material..)
230 auto metaobj = GetNativeObjectParam<META_NS::IMetadata>(args);
231
232 StoreJsObj(interface_pointer_cast<META_NS::IObject>(metaobj), meJs);
233
234 // check if it's a SCENE_NS::IShader (can only be set on instances created createShader, these are the "template"
235 // shaders.)
236 auto shader = interface_pointer_cast<SCENE_NS::IShader>(metaobj);
237 if (shader) {
238 // we should not be bound to a material then.. this is a place holder object.
239 SetNativeObject(interface_pointer_cast<META_NS::IObject>(shader), true);
240 } else {
241 // should be bound to a material..
242 // so the shader should be stored as a parameter..
243 SetNativeObject(interface_pointer_cast<META_NS::IObject>(metaobj), true);
244 shader = interface_pointer_cast<SCENE_NS::IShader>(metaobj->GetPropertyByName<IntfPtr>("shader")->GetValue());
245 }
246
247 NapiApi::Object material = args.Get<NapiApi::Object>("Material"); // see if we SHOULD be bound to a material.
248 if (material) {
249 BindToMaterial(meJs, material);
250 }
251
252 BASE_NS::string name;
253 if (auto prm = args.Get<BASE_NS::string>("name")) {
254 name = prm;
255 } else {
256 if (auto named = interface_cast<META_NS::INamed>(metaobj)) {
257 name = named->Name()->GetValue();
258 }
259 }
260 meJs.Set("name", name);
261 }
262
BindToMaterial(NapiApi::Object meJs,NapiApi::Object material)263 void ShaderJS::BindToMaterial(NapiApi::Object meJs, NapiApi::Object material)
264 {
265 auto metaobj = GetNativeMeta<META_NS::IMetadata>(meJs);
266 auto shader = interface_pointer_cast<SCENE_NS::IShader>(
267 metaobj->GetPropertyByName<IntfPtr>("shader")->GetValue());
268
269 napi_env e = meJs.GetEnv();
270 // inputs are actually owned (and used) by the material.
271 // create the input object
272 NapiApi::Object inputs(e);
273
274 auto* tro = material.Native<TrueRootObject>();
275 auto mat = interface_pointer_cast<SCENE_NS::IMaterial>(tro->GetNativeObject());
276
277 BASE_NS::vector<napi_property_descriptor> inputProps;
278
279 META_NS::IMetadata::Ptr customProperties;
280 BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr> Textures;
281
282 ExecSyncTask([mat, &customProperties, &Textures]() {
283 Textures = mat->Inputs()->GetValue();
284 customProperties = interface_pointer_cast<META_NS::IMetadata>(mat->CustomProperties()->GetValue());
285 return META_NS::IAny::Ptr {};
286 });
287 if (!Textures.empty()) {
288 int index = 0;
289 for (auto t : Textures) {
290 BASE_NS::string name;
291 auto nn = interface_cast<META_NS::INamed>(t);
292 if (nn) {
293 name = nn->Name()->GetValue();
294 } else {
295 name = "TextureInfo_" + BASE_NS::to_string(index);
296 }
297 BASE_NS::shared_ptr<Proxy> proxt;
298 // factor
299 proxt = BASE_NS::shared_ptr { new PropProxy<Vec4Proxy>(e, t->Factor(), name) };
300 if (proxt) {
301 proxies_.push_back(proxt);
302 proxt->insertProp(inputProps);
303 }
304 proxt = BASE_NS::shared_ptr { new BitmapProxy(scene_.GetObject(), t->Image(), name) };
305 if (proxt) {
306 proxies_.push_back(proxt);
307 proxt->insertProp(inputProps);
308 }
309
310 index++;
311 }
312 }
313 if (customProperties) {
314 for (auto t : customProperties->GetAllProperties()) {
315 auto name = t->GetName();
316 auto type = t->GetTypeId();
317 auto tst = type.ToString();
318 BASE_NS::shared_ptr<Proxy> proxt;
319 if (type == META_NS::UidFromType<float>()) {
320 proxt = BASE_NS::shared_ptr { new TypeProxy<float>(t) };
321 }
322 if (type == META_NS::UidFromType<int32_t>()) {
323 proxt = BASE_NS::shared_ptr { new TypeProxy<int32_t>(t) };
324 }
325 if (type == META_NS::UidFromType<uint32_t>()) {
326 proxt = BASE_NS::shared_ptr { new TypeProxy<uint32_t>(t) };
327 }
328 if (type == META_NS::UidFromType<BASE_NS::Math::Vec2>()) {
329 proxt = BASE_NS::shared_ptr { new PropProxy<Vec2Proxy>(e, t) };
330 }
331 if (type == META_NS::UidFromType<BASE_NS::Math::Vec3>()) {
332 proxt = BASE_NS::shared_ptr { new PropProxy<Vec3Proxy>(e, t) };
333 }
334 if (type == META_NS::UidFromType<BASE_NS::Math::Vec4>()) {
335 proxt = BASE_NS::shared_ptr { new PropProxy<Vec4Proxy>(e, t) };
336 }
337 if (proxt) {
338 proxies_.push_back(proxt);
339 proxt->insertProp(inputProps);
340 }
341 }
342 }
343 if (!inputProps.empty()) {
344 napi_define_properties(e, inputs, inputProps.size(), inputProps.data());
345 }
346
347 inputs_ = { e, inputs };
348 meJs.Set("inputs", inputs_.GetValue());
349 }
350
~ShaderJS()351 ShaderJS::~ShaderJS()
352 {
353 DisposeNative();
354 }
355
GetInstanceImpl(uint32_t id)356 void* ShaderJS::GetInstanceImpl(uint32_t id)
357 {
358 if (id == ShaderJS::ID) {
359 return this;
360 }
361 return SceneResourceImpl::GetInstanceImpl(id);
362 }
363
DisposeNative()364 void ShaderJS::DisposeNative()
365 {
366 if (!disposed_) {
367 disposed_ = true;
368 SetNativeObject(nullptr, false);
369 SetNativeObject(nullptr, true);
370 NapiApi::Object obj = scene_.GetObject();
371 if (obj) {
372 auto* tro = obj.Native<TrueRootObject>();
373
374 if (tro) {
375 SceneJS* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
376 sceneJS->ReleaseDispose((uintptr_t)&scene_);
377 }
378 }
379 proxies_.clear();
380 inputs_.Reset();
381 scene_.Reset();
382 }
383 }
Finalize(napi_env env)384 void ShaderJS::Finalize(napi_env env)
385 {
386 DisposeNative();
387 BaseObject<ShaderJS>::Finalize(env);
388 }
389