• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/builtin_objects.h>
19 #include <meta/interface/intf_task_queue.h>
20 #include <meta/interface/intf_task_queue_registry.h>
21 #include <meta/interface/property/construct_property.h>
22 #include <scene/interface/intf_shader.h>
23 #include <scene/interface/intf_scene.h>
24 #include <scene/interface/intf_material.h>
25 
26 #include "SceneJS.h"
27 using IntfPtr = META_NS::SharedPtrIInterface;
28 using IntfWeakPtr = META_NS::WeakPtrIInterface;
29 
BaseMaterial(MaterialType lt)30 BaseMaterial::BaseMaterial(MaterialType lt) : SceneResourceImpl(SceneResourceImpl::MATERIAL), materialType_(lt) {}
~BaseMaterial()31 BaseMaterial::~BaseMaterial() {}
Init(const char * class_name,napi_env env,napi_value exports,napi_callback ctor,BASE_NS::vector<napi_property_descriptor> & node_props)32 void BaseMaterial::Init(const char* class_name, napi_env env, napi_value exports, napi_callback ctor,
33     BASE_NS::vector<napi_property_descriptor>& node_props)
34 {
35     SceneResourceImpl::GetPropertyDescs(node_props);
36 
37     using namespace NapiApi;
38     node_props.emplace_back(TROGetProperty<uint32_t, BaseMaterial, &BaseMaterial::GetMaterialType>("materialType"));
39     node_props.emplace_back(
40         TROGetSetProperty<bool, BaseMaterial, &BaseMaterial::GetShadowReceiver, &BaseMaterial::SetShadowReceiver>(
41             "shadowReceiver"));
42     node_props.emplace_back(
43         TROGetSetProperty<uint32_t, BaseMaterial, &BaseMaterial::GetCullMode, &BaseMaterial::SetCullMode>(
44             "cullMode"));
45     node_props.emplace_back(
46         TROGetSetProperty<NapiApi::Object, BaseMaterial, &BaseMaterial::GetBlend, &BaseMaterial::SetBlend>(
47             "blend"));
48     node_props.emplace_back(
49         TROGetSetProperty<float, BaseMaterial, &BaseMaterial::GetAlphaCutoff, &BaseMaterial::SetAlphaCutoff>(
50             "alphaCutoff"));
51     node_props.emplace_back(
52         TROGetSetProperty<NapiApi::Object, BaseMaterial, &BaseMaterial::GetRenderSort, &BaseMaterial::SetRenderSort>(
53             "renderSort"));
54 
55     napi_value func;
56     auto status = napi_define_class(
57         env, class_name, NAPI_AUTO_LENGTH, ctor, nullptr, node_props.size(), node_props.data(), &func);
58 
59     NapiApi::MyInstanceState* mis;
60     GetInstanceData(env, (void**)&mis);
61     mis->StoreCtor(class_name, func);
62 
63     NapiApi::Object exp1(env, exports);
64     napi_value eType1;
65     napi_value v1;
66     napi_create_object(env, &eType1);
67 #define DECL_ENUM(enu, x)                         \
68     napi_create_uint32(env, MaterialType::x, &v1); \
69     napi_set_named_property(env, enu, #x, v1);
70 
71     DECL_ENUM(eType1, SHADER);
72     DECL_ENUM(eType1, METALLIC_ROUGHNESS);
73 #undef DECL_ENUM
74     exp1.Set("MaterialType", eType1);
75 
76     NapiApi::Object exp2(env, exports);
77     napi_value eType2;
78     napi_value v2;
79     napi_create_object(env, &eType2);
80 #define DECL_ENUM(enu, x)                         \
81     napi_create_uint32(env, CullMode::x, &v2); \
82     napi_set_named_property(env, enu, #x, v2);
83 
84     DECL_ENUM(eType2, NONE);
85     DECL_ENUM(eType2, FRONT);
86     DECL_ENUM(eType2, BACK);
87 #undef DECL_ENUM
88     exp2.Set("CullMode", eType2);
89 }
90 
GetInstanceImpl(uint32_t id)91 void* BaseMaterial::GetInstanceImpl(uint32_t id)
92 {
93     if (id == BaseMaterial::ID)
94         return (BaseMaterial*)this;
95     return SceneResourceImpl::GetInstanceImpl(id);
96 }
DisposeNative(TrueRootObject * tro)97 void BaseMaterial::DisposeNative(TrueRootObject* tro)
98 {
99     // do nothing for now..
100     LOG_V("BaseMaterial::DisposeNative");
101     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(tro->GetNativeObject())) {
102         // reset the native object refs
103         tro->SetNativeObject(nullptr, false);
104         tro->SetNativeObject(nullptr, true);
105     }
106     scene_.Reset();
107 }
GetMaterialType(NapiApi::FunctionContext<> & ctx)108 napi_value BaseMaterial::GetMaterialType(NapiApi::FunctionContext<>& ctx)
109 {
110     if (!validateSceneRef()) {
111         return ctx.GetUndefined();
112     }
113     uint32_t type = -1; // return -1 if the object does not exist anymore
114     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
115         type = META_NS::GetValue(material->Type()) == SCENE_NS::MaterialType::METALLIC_ROUGHNESS
116                    ? BaseMaterial::METALLIC_ROUGHNESS
117                    : BaseMaterial::SHADER;
118     }
119     return ctx.GetNumber(type);
120 }
GetShadowReceiver(NapiApi::FunctionContext<> & ctx)121 napi_value BaseMaterial::GetShadowReceiver(NapiApi::FunctionContext<>& ctx)
122 {
123     if (!validateSceneRef()) {
124         return ctx.GetUndefined();
125     }
126     bool shadowReceiver = false;
127     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
128         uint32_t lightingFlags = uint32_t(META_NS::GetValue(material->LightingFlags()));
129         shadowReceiver = lightingFlags & uint32_t(SCENE_NS::LightingFlags::SHADOW_RECEIVER_BIT);
130     }
131     return ctx.GetBoolean(shadowReceiver);
132 }
SetShadowReceiver(NapiApi::FunctionContext<bool> & ctx)133 void BaseMaterial::SetShadowReceiver(NapiApi::FunctionContext<bool>& ctx)
134 {
135     if (!validateSceneRef()) {
136         return;
137     }
138     bool shadowReceiver = ctx.Arg<0>();
139     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
140         uint32_t lightingFlags = uint32_t(META_NS::GetValue(material->LightingFlags()));
141         if (shadowReceiver) {
142             lightingFlags |= uint32_t(SCENE_NS::LightingFlags::SHADOW_RECEIVER_BIT);
143         } else {
144             lightingFlags &= ~uint32_t(SCENE_NS::LightingFlags::SHADOW_RECEIVER_BIT);
145         }
146         META_NS::SetValue(material->LightingFlags(), static_cast<SCENE_NS::LightingFlags>(lightingFlags));
147     }
148 }
GetCullMode(NapiApi::FunctionContext<> & ctx)149 napi_value BaseMaterial::GetCullMode(NapiApi::FunctionContext<>& ctx)
150 {
151     if (!validateSceneRef()) {
152         return ctx.GetUndefined();
153     }
154     auto cullMode = SCENE_NS::CullModeFlags::NONE;
155     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
156         if (auto shader = META_NS::GetValue(material->MaterialShader())) {
157             if (auto scene = GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
158                 cullMode = static_cast<SCENE_NS::CullModeFlags>(shader->GetCullMode().GetResult());
159             }
160         }
161     }
162     return ctx.GetNumber(static_cast<uint32_t>(cullMode));
163 }
SetCullMode(NapiApi::FunctionContext<uint32_t> & ctx)164 void BaseMaterial::SetCullMode(NapiApi::FunctionContext<uint32_t>& ctx)
165 {
166     if (!validateSceneRef()) {
167         return;
168     }
169     auto cullMode = static_cast<SCENE_NS::CullModeFlags>((uint32_t)ctx.Arg<0>());
170 
171     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
172         if (auto shader = META_NS::GetValue(material->MaterialShader())) {
173             shader->SetCullMode(cullMode);
174             // need to forcefully refresh the material, otherwise renderer will ignore the change
175             auto val = META_NS::GetValue(material->MaterialShader());
176             META_NS::SetValue(material->MaterialShader(), val);
177         }
178     }
179 }
GetBlend(NapiApi::FunctionContext<> & ctx)180 napi_value BaseMaterial::GetBlend(NapiApi::FunctionContext<>& ctx)
181 {
182     if (!validateSceneRef()) {
183         return ctx.GetUndefined();
184     }
185 
186     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
187     if (!material) {
188         return ctx.GetUndefined();
189     }
190 
191     auto shader = META_NS::GetValue(material->MaterialShader());
192     if (!shader) {
193         return ctx.GetUndefined();
194     }
195 
196     bool enableBlend = shader->IsBlendEnabled().GetResult();
197 
198     NapiApi::Object blendObjectJS(ctx.Env());
199     blendObjectJS.Set("enabled", ctx.GetBoolean(enableBlend));
200     return blendObjectJS.ToNapiValue();
201 }
SetBlend(NapiApi::FunctionContext<NapiApi::Object> & ctx)202 void BaseMaterial::SetBlend(NapiApi::FunctionContext<NapiApi::Object>& ctx)
203 {
204     if (!validateSceneRef()) {
205         return;
206     }
207 
208     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
209     if (!material) {
210         return;
211     }
212 
213     auto shader = META_NS::GetValue(material->MaterialShader());
214     if (!shader) {
215         return;
216     }
217 
218     NapiApi::Object blendObjectJS = ctx.Arg<0>();
219     bool enableBlend = blendObjectJS.Get<bool>("enabled");
220     shader->EnableBlend(enableBlend);
221 
222     // need to forcefully refresh the material, otherwise renderer will ignore the change
223     auto val = META_NS::GetValue(material->MaterialShader());
224     META_NS::SetValue(material->MaterialShader(), val);
225 }
GetAlphaCutoff(NapiApi::FunctionContext<> & ctx)226 napi_value BaseMaterial::GetAlphaCutoff(NapiApi::FunctionContext<>& ctx)
227 {
228     if (!validateSceneRef()) {
229         return ctx.GetUndefined();
230     }
231     float alphaCutoff = 1.0f;
232     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
233         alphaCutoff = META_NS::GetValue(material->AlphaCutoff());
234     }
235     return ctx.GetNumber(alphaCutoff);
236 }
SetAlphaCutoff(NapiApi::FunctionContext<float> & ctx)237 void BaseMaterial::SetAlphaCutoff(NapiApi::FunctionContext<float>& ctx)
238 {
239     if (!validateSceneRef()) {
240         return;
241     }
242     float alphaCutoff = ctx.Arg<0>();
243 
244     if (auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx))) {
245         META_NS::SetValue(material->AlphaCutoff(), alphaCutoff);
246     }
247 }
GetRenderSort(NapiApi::FunctionContext<> & ctx)248 napi_value BaseMaterial::GetRenderSort(NapiApi::FunctionContext<>& ctx)
249 {
250     if (!validateSceneRef()) {
251         return ctx.GetUndefined();
252     }
253 
254     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
255     if (!material) {
256         return ctx.GetUndefined();
257     }
258 
259     auto renderSort = META_NS::GetValue(material->RenderSort());
260 
261     NapiApi::Object renderSortJS(ctx.Env());
262     renderSortJS.Set("renderSortLayer", ctx.GetNumber(renderSort.renderSortLayer));
263     renderSortJS.Set("renderSortLayerOrder", ctx.GetNumber(renderSort.renderSortLayerOrder));
264     return renderSortJS.ToNapiValue();
265 }
SetRenderSort(NapiApi::FunctionContext<NapiApi::Object> & ctx)266 void BaseMaterial::SetRenderSort(NapiApi::FunctionContext<NapiApi::Object>& ctx)
267 {
268     if (!validateSceneRef()) {
269         return;
270     }
271 
272     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
273     if (!material) {
274         return;
275     }
276 
277     SCENE_NS::RenderSort renderSort;
278     NapiApi::Object renderSortJS = ctx.Arg<0>();
279     renderSort.renderSortLayer = renderSortJS.Get<uint32_t>("renderSortLayer");
280     renderSort.renderSortLayerOrder = renderSortJS.Get<uint32_t>("renderSortLayerOrder");
281     META_NS::SetValue(material->RenderSort(), renderSort);
282 }
283 
Init(napi_env env,napi_value exports)284 void MaterialJS::Init(napi_env env, napi_value exports)
285 {
286 #define MRADDPROP(index, name)                           \
287     NapiApi::GetSetProperty<NapiApi::Object, MaterialJS, \
288         &MaterialJS::GetMaterialProperty<index>, \
289         &MaterialJS::SetMaterialProperty<index>>(name)
290 
291     BASE_NS::vector<napi_property_descriptor> props = {
292         NapiApi::GetSetProperty<NapiApi::Object, MaterialJS, &MaterialJS::GetColorShader,
293             &MaterialJS::SetColorShader>("colorShader"),
294         MRADDPROP(0, "baseColor"),
295         MRADDPROP(1, "normal"),
296         MRADDPROP(2, "material"),
297         MRADDPROP(3, "emissive"),
298         MRADDPROP(4, "ambientOcclusion"),
299         MRADDPROP(5, "clearCoat"),
300         MRADDPROP(6, "clearCoatRoughness"),
301         MRADDPROP(7, "clearCoatNormal"),
302         MRADDPROP(8, "sheen"),
303         MRADDPROP(10, "specular")
304     };
305     #undef MRADDPROP
306 
307     BaseMaterial::Init("Material", env, exports, BaseObject::ctor<MaterialJS>(), props);
308 }
309 
MaterialJS(napi_env e,napi_callback_info i)310 MaterialJS::MaterialJS(napi_env e, napi_callback_info i)
311     : BaseObject<MaterialJS>(e, i), BaseMaterial(BaseMaterial::MaterialType::METALLIC_ROUGHNESS)
312 {
313     NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
314     NapiApi::Object meJs(fromJs.This());
315 
316     NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene... (do i need it here?)
317     NapiApi::Object args = fromJs.Arg<1>();  // other args
318 
319     scene_ = scene;
320     if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
321         LOG_F("INVALID SCENE!");
322     }
323 
324     if (auto sceneJS = GetJsWrapper<SceneJS>(scene)) {
325         sceneJS->DisposeHook(reinterpret_cast<uintptr_t>(&scene_), meJs);
326     }
327 
328     auto metaobj = GetNativeObjectParam<META_NS::IObject>(args); // Should be IMaterial
329     if (auto material = interface_cast<SCENE_NS::IMaterial>(metaobj)) {
330         materialType_ = META_NS::GetValue(material->Type()) == SCENE_NS::MaterialType::CUSTOM ?
331             MaterialType::SHADER : MaterialType::METALLIC_ROUGHNESS;
332     }
333     SetNativeObject(metaobj, true);
334     StoreJsObj(metaobj, meJs);
335 
336     BASE_NS::string name;
337     if (auto prm = args.Get<BASE_NS::string>("name"); prm.IsDefined()) {
338         name = prm;
339     } else {
340         if (auto named = interface_cast<META_NS::IObject>(metaobj)) {
341             name = named->GetName();
342         }
343     }
344     meJs.Set("name", name);
345 }
346 
~MaterialJS()347 MaterialJS::~MaterialJS() {}
GetInstanceImpl(uint32_t id)348 void* MaterialJS::GetInstanceImpl(uint32_t id)
349 {
350     if (id == MaterialJS::ID)
351         return this;
352     return BaseMaterial::GetInstanceImpl(id);
353 }
DisposeNative(void * scn)354 void MaterialJS::DisposeNative(void* scn)
355 {
356     if (disposed_) {
357         return;
358     }
359     disposed_ = true;
360     if (auto* sceneJS = static_cast<SceneJS*>(scn)) {
361         sceneJS->ReleaseDispose(reinterpret_cast<uintptr_t>(&scene_));
362     }
363     SetNativeObject(nullptr, false);
364     SetNativeObject(nullptr, true);
365     shaderBind_.reset();
366     shader_.Reset();
367 
368     BaseMaterial::DisposeNative(this);
369 }
Finalize(napi_env env)370 void MaterialJS::Finalize(napi_env env)
371 {
372     DisposeNative(GetJsWrapper<SceneJS>(scene_.GetObject()));
373     BaseObject::Finalize(env);
374 }
SetColorShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)375 void MaterialJS::SetColorShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
376 {
377     if (!validateSceneRef()) {
378         // owning scene has been destroyed.
379         // the material etc should also be gone.
380         // but there is a possible issue where the native object is still alive.
381 
382         // most likely could happen if scene.dispose is not called
383         // but all references to the scene have been released,
384         // and garbage collection may or may not have been done yet. (or is partially done)
385         // if the scene is garbage collected then all the resources should be disposed.
386         return;
387     }
388 
389     NapiApi::Object shaderJS = ctx.Arg<0>();
390     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
391     if (!material) {
392         // material destroyed, just make sure we have no shader reference anymore.
393         shader_.Reset();
394         return;
395     }
396     auto shader = GetNativeMeta<SCENE_NS::IShader>(shaderJS);
397     materialType_ = shader ? MaterialType::SHADER : MaterialType::METALLIC_ROUGHNESS;
398     META_NS::SetValue(material->Type(), shader ? SCENE_NS::MaterialType::CUSTOM :
399         SCENE_NS::MaterialType::METALLIC_ROUGHNESS);
400 
401     // bind it to material (in native)
402     material->MaterialShader()->SetValue(shader);
403 
404     if (!shader) {
405         shader_.Reset();
406         return;
407     }
408 
409     // construct a "bound" shader object from the "non bound" one.
410     NapiApi::Env env(ctx.Env());
411     NapiApi::Object parms(env);
412     napi_value args[] = {
413         scene_.GetValue(),  // bind the new instance of the shader to this javascript scene object
414         parms.ToNapiValue() // other constructor parameters
415     };
416 
417     parms.Set("name", shaderJS.Get("name"));
418     parms.Set("Material", ctx.This()); // js material object that we are bound to.
419 
420     shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
421     interface_cast<META_NS::IMetadata>(shaderBind_)
422         ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
423             "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
424     interface_cast<META_NS::IMetadata>(shaderBind_)
425         ->GetProperty<IntfPtr>("shader")
426         ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
427 
428     auto argc = BASE_NS::countof(args);
429     auto argv = args;
430     MakeNativeObjectParam(env, shaderBind_, argc, argv);
431     auto result = CreateJsObj(env, "Shader", shaderBind_, false, argc, argv);
432     shader_ = NapiApi::StrongRef(StoreJsObj(shaderBind_, result));
433 }
GetColorShader(NapiApi::FunctionContext<> & ctx)434 napi_value MaterialJS::GetColorShader(NapiApi::FunctionContext<>& ctx)
435 {
436     if (!validateSceneRef()) {
437         return ctx.GetUndefined();
438     }
439 
440     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetNativeObject());
441     if (!material) {
442         // material destroyed, just make sure we have no shader reference anymore.
443         shader_.Reset();
444         return ctx.GetNull();
445     }
446 
447     if (shader_.IsEmpty()) {
448         // no shader set yet..
449         // see if we have one on the native side.
450         // and create the "bound shader" object from it.
451 
452         // check native side..
453         SCENE_NS::IShader::Ptr shader = material->MaterialShader()->GetValue();
454         if (!shader) {
455             // no shader in native also.
456             return ctx.GetNull();
457         }
458 
459         // construct a "bound" shader object from the "non bound" one.
460         NapiApi::Env env(ctx.Env());
461         NapiApi::Object parms(env);
462         napi_value args[] = {
463             scene_.GetValue(),  // bind the new instance of the shader to this javascript scene object
464             parms.ToNapiValue() // other constructor parameters
465         };
466 
467         if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
468             LOG_F("INVALID SCENE!");
469         }
470         parms.Set("Material", ctx.This()); // js material object that we are bound to.
471 
472         shaderBind_ = META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object);
473         interface_cast<META_NS::IMetadata>(shaderBind_)
474             ->AddProperty(META_NS::ConstructProperty<IntfPtr>(
475                 "shader", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
476         interface_cast<META_NS::IMetadata>(shaderBind_)
477             ->GetProperty<IntfPtr>("shader")
478             ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(shader));
479 
480         auto argc = BASE_NS::countof(args);
481         auto argv = args;
482         MakeNativeObjectParam(env, shaderBind_, argc, argv);
483         auto result = CreateJsObj(env, "Shader", shaderBind_, false, argc, argv);
484         shader_ = NapiApi::StrongRef(StoreJsObj(shaderBind_, result));
485     }
486     return shader_.GetValue();
487 }
488 
489 template<size_t Index>
GetMaterialProperty(NapiApi::FunctionContext<> & ctx)490 napi_value MaterialJS::GetMaterialProperty(NapiApi::FunctionContext<>& ctx)
491 {
492     if (!validateSceneRef()) {
493         return ctx.GetUndefined();
494     }
495     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
496     if (!material || !material->Textures()) {
497         return ctx.GetUndefined();
498     }
499     SCENE_NS::ITexture::Ptr texture = material->Textures()->GetValueAt(Index);
500     if (!texture) {
501         return ctx.GetUndefined();
502     }
503 
504     auto obj = interface_pointer_cast<META_NS::IObject>(texture);
505     if (auto cached = FetchJsObj(obj)) {
506         // always return the same js object.
507         return cached.ToNapiValue();
508     }
509 
510     napi_value args[] = { ctx.This().ToNapiValue() };
511     MakeNativeObjectParam(ctx.GetEnv(), texture, BASE_NS::countof(args), args);
512 
513     auto result = CreateJsObj(ctx.GetEnv(), "MaterialProperty", obj, true, BASE_NS::countof(args), args);
514     auto materialPropertyJs = StoreJsObj(obj, result);
515     return materialPropertyJs.ToNapiValue();
516 }
517 template<size_t Index>
SetMaterialProperty(NapiApi::FunctionContext<NapiApi::Object> & ctx)518 void MaterialJS::SetMaterialProperty(NapiApi::FunctionContext<NapiApi::Object>& ctx)
519 {
520     if (!validateSceneRef()) {
521         return;
522     }
523     auto material = interface_pointer_cast<SCENE_NS::IMaterial>(GetThisNativeObject(ctx));
524     if (!material || !material->Textures()) {
525         return;
526     }
527     SCENE_NS::ITexture::Ptr texture = material->Textures()->GetValueAt(Index);
528     if (!texture) {
529         return;
530     }
531 
532     NapiApi::Object arg = ctx.Arg<0>();
533     if (auto etex = GetNativeObjectParam<SCENE_NS::ITexture>(arg)) {
534         // Copy the exposed data from the given texture
535         META_NS::SetValue(texture->Image(), META_NS::GetValue(etex->Image()));
536         META_NS::SetValue(texture->Factor(), META_NS::GetValue(etex->Factor()));
537     }
538 }
539